You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by kn...@apache.org on 2009/03/31 22:49:14 UTC

svn commit: r760608 - in /wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket: page/persistent/disk/DiskDataStore.java protocol/http/WebApplication.java protocol/http/WicketFilter.java

Author: knopp
Date: Tue Mar 31 20:49:14 2009
New Revision: 760608

URL: http://svn.apache.org/viewvc?rev=760608&view=rev
Log: (empty)

Added:
    wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java   (with props)
Modified:
    wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
    wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WicketFilter.java

Added: wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java?rev=760608&view=auto
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java (added)
+++ wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java Tue Mar 31 20:49:14 2009
@@ -0,0 +1,375 @@
+package org.apache.wicket.page.persistent.disk;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.WicketRuntimeException;
+import org.apache.wicket.page.persistent.DataStore;
+import org.apache.wicket.page.persistent.disk.PageWindowManager.PageWindow;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DiskDataStore implements DataStore
+{
+	private final String applicationName;
+	private final int maxSizePerPageSession;
+	private final FileChannelPool fileChannelPool;
+	private final File fileStoreFolder;
+	private final ConcurrentMap<String, SessionEntry> sessionEntryMap = new ConcurrentHashMap<String, SessionEntry>();
+
+	public DiskDataStore(String applicationName, File fileStoreFolder, int maxSizePerSession,
+			int fileChannelPoolCapacity)
+	{
+		this.applicationName = applicationName;
+		this.fileStoreFolder = fileStoreFolder;
+		this.maxSizePerPageSession = maxSizePerSession;
+		this.fileChannelPool = new FileChannelPool(fileChannelPoolCapacity);
+	}
+
+	public DiskDataStore(String applicationName, int maxSizePerSession, int fileChannelPoolCapacity)
+	{
+		this(applicationName, getDefaultFileStoreFolder(), maxSizePerSession, fileChannelPoolCapacity);
+	}
+
+	public void destroy()
+	{
+		fileChannelPool.destroy();
+	}
+
+	public byte[] getData(String sessionId, int id)
+	{
+		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
+		if (sessionEntry != null)
+		{
+			return sessionEntry.loadPage(id);
+		}
+		else
+		{
+			return null;	
+		}		
+	}
+
+	public boolean isReplicated()
+	{
+		return false;
+	}
+
+	public void removeData(String sessionId, int id)
+	{
+		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
+		if (sessionEntry != null)
+		{
+			sessionEntry.removePage(id);
+		}
+	}
+
+	public void removeData(String sessionId)
+	{
+		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
+		if (sessionEntry != null)
+		{
+			synchronized (sessionEntry)
+			{
+				sessionEntryMap.remove(sessionEntry);
+				sessionEntry.unbind();	
+			}			
+		}
+	}
+
+	public void storeData(String sessionId, int id, byte[] data)
+	{
+		SessionEntry sessionEntry = getSessionEntry(sessionId, false);
+		if (sessionEntry != null)
+		{
+			sessionEntry.savePage(id, data);
+		}
+	}
+	
+	private SessionEntry getSessionEntry(String sessionId, boolean create)
+	{
+		if (!create)
+		{
+			return sessionEntryMap.get(sessionId);
+		}
+		else
+		{
+			SessionEntry entry = new SessionEntry(this, sessionId);
+			entry = sessionEntryMap.putIfAbsent(sessionId, entry);
+			return entry;
+		}
+	}
+
+	protected static class SessionEntry implements Serializable
+	{
+		private static final long serialVersionUID = 1L;
+
+		private final String sessionId;
+		private transient DiskDataStore diskDataStore;
+		private String fileName;
+		private PageWindowManager manager;
+		private boolean unbound = false;
+
+		protected SessionEntry(DiskDataStore diskDataStore, String sessionId)
+		{
+			this.diskDataStore = diskDataStore;			
+			this.sessionId = sessionId;
+		}
+		
+		private PageWindowManager getManager()
+		{
+			if (manager == null)
+			{
+				manager = new PageWindowManager(diskDataStore.maxSizePerPageSession);;
+			}
+			return manager;
+		}
+		
+		private String getFileName()
+		{
+			if (fileName == null)
+			{
+				fileName = diskDataStore.getSessionFileName(sessionId, true);; 
+			}
+			return fileName;
+		}
+		
+		/**
+		 * @return session id
+		 */
+		public String getSessionId()
+		{
+			return sessionId;
+		}
+
+		/**
+		 * Saves the serialized page to appropriate file.
+		 * 
+		 * @param page
+		 */
+		public synchronized void savePage(int pageId, byte data[])
+		{
+			if (unbound)
+			{
+				return;
+			}
+			// only save page that has some data
+			if (data != null)
+			{
+				// allocate window for page
+				PageWindow window = getManager().createPageWindow(pageId, data.length);
+
+				// take the filechannel from the pool
+				FileChannel channel = diskDataStore.fileChannelPool.getFileChannel(getFileName(), true);
+				try
+				{
+					// write the content
+					channel.write(ByteBuffer.wrap(data), window.getFilePartOffset());
+				}
+				catch (IOException e)
+				{
+					log.error("Error writing to a channel " + channel, e);
+				}
+				finally
+				{
+					// return the "borrowed" file channel
+					diskDataStore.fileChannelPool.returnFileChannel(channel);
+				}
+			}
+		}
+
+		/**
+		 * Removes the page from pagemap file.
+		 * 
+		 * @param pageMapName
+		 * @param pageId
+		 */
+		public synchronized void removePage(int pageId)
+		{
+			if (unbound)
+			{
+				return;
+			}
+			getManager().removePage(pageId);
+		}
+
+		/**
+		 * Loads the part of pagemap file specified by the given PageWindow.
+		 * 
+		 * @param window
+		 * @param pageMapFileName
+		 * @return serialized page data
+		 */
+		public byte[] loadPage(PageWindow window)
+		{			
+			byte[] result = null;
+			FileChannel channel = diskDataStore.fileChannelPool.getFileChannel(getFileName(), false);
+			if (channel != null)
+			{
+				ByteBuffer buffer = ByteBuffer.allocate(window.getFilePartSize());
+				try
+				{
+					channel.read(buffer, window.getFilePartOffset());
+					if (buffer.hasArray())
+					{
+						result = buffer.array();
+					}
+				}
+				catch (IOException e)
+				{
+					log.error("Error reading from file channel " + channel, e);
+				}
+				finally
+				{
+					diskDataStore.fileChannelPool.returnFileChannel(channel);
+				}
+			}
+			return result;
+		}
+		
+		/**
+		 * Loads the specified page data.
+		 * 
+		 * @param pageMapName
+		 * @param id
+		 * @return page data or null if the page is no longer in pagemap file
+		 */
+		public synchronized byte[] loadPage(int id)
+		{
+			if (unbound)
+			{
+				return null;
+			}
+			byte[] result = null;
+			PageWindow window = getManager().getPageWindow(id);
+			if (window != null)
+			{
+				result = loadPage(window);
+			}
+			return result;
+		}
+		
+		/**
+		 * Deletes all files for this session.
+		 */
+		public synchronized void unbind()
+		{
+			diskDataStore.fileChannelPool.closeAndDeleteFileChannel(getFileName());
+			File sessionFolder = diskDataStore.getSessionFolder(sessionId, false);
+			if (sessionFolder.exists())
+			{
+				sessionFolder.delete();
+			}
+			unbound = true;
+		}
+	}
+
+	/**
+	 * Returns the file name for specified session. If the session folder (folder that contains the
+	 * file) does not exist and createSessionFolder is true, the folder will be created.
+	 * 
+	 * @param sessionId
+	 * @param pageMapName
+	 * @param createSessionFolder
+	 * @return file name for pagemap
+	 */
+	private String getSessionFileName(String sessionId, boolean createSessionFolder)
+	{
+		File sessionFolder = getSessionFolder(sessionId, createSessionFolder);
+		return new File(sessionFolder, "data").getAbsolutePath();
+	}
+
+	private static File getDefaultFileStoreFolder()
+	{
+		File dir = null;
+
+		if (Application.exists())
+		{
+			dir = (File) ((WebApplication) Application.get()).getServletContext().getAttribute(
+					"javax.servlet.context.tempdir");
+		}
+
+		if (dir != null)
+		{
+			return dir;
+		}
+		else
+		{
+			try
+			{
+				return File.createTempFile("file-prefix", null).getParentFile();
+			}
+			catch (IOException e)
+			{
+				throw new WicketRuntimeException(e);
+			}
+		}
+	}
+
+	private File getStoreFolder()
+	{
+		File storeFolder = new File(fileStoreFolder, applicationName + "-filestore");
+		return storeFolder;
+	}
+
+	/**
+	 * Returns the folder for the specified sessions. If the folder doesn't exist and the create
+	 * flag is set, the folder will be created.
+	 * 
+	 * @param sessionId
+	 * @param create
+	 * @return folder used to store session data
+	 */
+	protected File getSessionFolder(String sessionId, boolean create)
+	{
+		File storeFolder = getStoreFolder();
+
+		sessionId = sessionId.replace('*', '_');
+		sessionId = sessionId.replace('/', '_');
+		sessionId = sessionId.replace(':', '_');
+
+		File sessionFolder = new File(storeFolder, sessionId);
+		if (create && sessionFolder.exists() == false)
+		{
+			mkdirs(sessionFolder);
+		}
+		return sessionFolder;
+	}
+
+	/**
+	 * Utility method for creating a directory
+	 * 
+	 * @param file
+	 */
+	private void mkdirs(File file)
+	{
+		// for some reason, simple file.mkdirs sometimes fails under heavy load
+		for (int j = 0; j < 5; ++j)
+		{
+			for (int i = 0; i < 10; ++i)
+			{
+				if (file.mkdirs())
+				{
+					return;
+				}
+			}
+			try
+			{
+				Thread.sleep(100);
+			}
+			catch (InterruptedException ignore)
+			{
+			}
+		}
+		log.error("Failed to make directory " + file);
+	}
+
+	private static final Logger log = LoggerFactory.getLogger(DiskDataStore.class);
+
+}

Propchange: wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/page/persistent/disk/DiskDataStore.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WebApplication.java?rev=760608&r1=760607&r2=760608&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WebApplication.java (original)
+++ wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WebApplication.java Tue Mar 31 20:49:14 2009
@@ -19,6 +19,8 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import javax.servlet.ServletContext;
+
 import org.apache.wicket.Application;
 import org.apache.wicket.DefaultPageFactory;
 import org.apache.wicket.IPage;
@@ -235,4 +237,28 @@
 		String key = sessionId + url.toString();
 		storedResponses.put(key, response);
 	}
+	
+	/**
+	 * Gets the servlet context for this application. Use this to get references to absolute paths,
+	 * global web.xml parameters (&lt;context-param&gt;), etc.
+	 * 
+	 * @return The servlet context for this application
+	 */
+	public ServletContext getServletContext()
+	{
+		if (wicketFilter != null)
+		{
+			return wicketFilter.getFilterConfig().getServletContext();
+		}
+		throw new IllegalStateException("servletContext is not set yet. Any code in your"
+			+ " Application object that uses the wicket filter instance should be put"
+			+ " in the init() method instead of your constructor");
+	}
+	
+	public void setWicketFilter(WicketFilter wicketFilter)
+	{
+		this.wicketFilter = wicketFilter;
+	}
+	
+	private WicketFilter wicketFilter;
 }

Modified: wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WicketFilter.java
URL: http://svn.apache.org/viewvc/wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WicketFilter.java?rev=760608&r1=760607&r2=760608&view=diff
==============================================================================
--- wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WicketFilter.java (original)
+++ wicket/sandbox/knopp/experimental/wicket-ng/src/main/java/org/apache/wicket/protocol/http/WicketFilter.java Tue Mar 31 20:49:14 2009
@@ -218,6 +218,7 @@
 		IWebApplicationFactory factory = getApplicationFactory();
 		webApplication = factory.createApplication(this);
 		webApplication.setName(filterConfig.getFilterName());
+		webApplication.setWicketFilter(this);
 		
 		initFilterPath();