All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.pageStore.AsynchronousDataStore Maven / Gradle / Ivy

Go to download

Wicket is a Java web application framework that takes simplicity, separation of concerns and ease of development to a whole new level. Wicket pages can be mocked up, previewed and later revised using standard WYSIWYG HTML design tools. Dynamic content processing and form handling is all handled in Java code using a first-class component model backed by POJO data beans that can easily be persisted using your favorite technology.

There is a newer version: 10.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.wicket.pageStore;

import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.apache.wicket.util.lang.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Facade for {@link IDataStore} that does the actual saving in worker thread.
 * 

* Creates an {@link Entry} for each triple (sessionId, pageId, data) and puts it in * {@link #entries} queue if there is room. Acts as producer.
* Later {@link PageSavingRunnable} reads in blocking manner from {@link #entries} and saves each * entry. Acts as consumer. *

* It starts only one instance of {@link PageSavingRunnable} because all we need is to make the page * storing asynchronous. We don't want to write concurrently in the wrapped {@link IDataStore}, * though it may happen in the extreme case when the queue is full. These cases should be avoided. * * @author Matej Knopp */ public class AsynchronousDataStore implements IDataStore { /** Log for reporting. */ private static final Logger log = LoggerFactory.getLogger(AsynchronousDataStore.class); /** * The time to wait when adding an {@link Entry} into the entries. In millis. */ private static final long OFFER_WAIT = 30L; /** * The time to wait for an entry to save with the wrapped {@link IDataStore}. In millis. */ private static final long POLL_WAIT = 1000L; /** * The page saving thread. */ private final Thread pageSavingThread; /** * The wrapped {@link IDataStore} that actually stores that pages */ private final IDataStore dataStore; /** * The queue where the entries which have to be saved are temporary stored */ private final BlockingQueue entries; /** * A map 'sessionId:::pageId' -> {@link Entry}. Used for fast retrieval of {@link Entry}s which * are not yet stored by the wrapped {@link IDataStore} */ private final ConcurrentMap entryMap; /** * Construct. * * @param dataStore * the wrapped {@link IDataStore} that actually saved the data * @param capacity * the capacity of the queue that delays the saving */ public AsynchronousDataStore(final IDataStore dataStore, final int capacity) { this.dataStore = dataStore; entries = new LinkedBlockingQueue(capacity); entryMap = new ConcurrentHashMap(); PageSavingRunnable savingRunnable = new PageSavingRunnable(dataStore, entries, entryMap); pageSavingThread = new Thread(savingRunnable, "Wicket-PageSavingThread"); pageSavingThread.setDaemon(true); pageSavingThread.start(); } /** * @see org.apache.wicket.pageStore.IDataStore#destroy() */ @Override public void destroy() { if (pageSavingThread.isAlive()) { pageSavingThread.interrupt(); try { pageSavingThread.join(); } catch (InterruptedException e) { log.error(e.getMessage(), e); } } dataStore.destroy(); } /** * Little helper * * @param sessionId * @param id * @return Entry */ private Entry getEntry(final String sessionId, final int id) { return entryMap.get(getKey(sessionId, id)); } /** * @see org.apache.wicket.pageStore.IDataStore#getData(java.lang.String, int) */ @Override public byte[] getData(final String sessionId, final int id) { Entry entry = getEntry(sessionId, id); if (entry != null) { log.debug( "Returning the data of a non-stored entry with sessionId '{}' and pageId '{}'", sessionId, id); return entry.data; } byte[] data = dataStore.getData(sessionId, id); log.debug("Returning the data of a stored entry with sessionId '{}' and pageId '{}'", sessionId, id); return data; } /** * @see org.apache.wicket.pageStore.IDataStore#isReplicated() */ @Override public boolean isReplicated() { return dataStore.isReplicated(); } /** * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String, int) */ @Override public void removeData(final String sessionId, final int id) { String key = getKey(sessionId, id); if (key != null) { Entry entry = entryMap.remove(key); if (entry != null) { entries.remove(entry); } } dataStore.removeData(sessionId, id); } /** * @see org.apache.wicket.pageStore.IDataStore#removeData(java.lang.String) */ @Override public void removeData(final String sessionId) { for (Iterator itor = entries.iterator(); itor.hasNext();) { Entry entry = itor.next(); if (entry != null) // this check is not needed in JDK6 { String entrySessionId = entry.sessionId; if (sessionId.equals(entrySessionId)) { entryMap.remove(getKey(entry)); itor.remove(); } } } dataStore.removeData(sessionId); } /** * Save the entry in the queue if there is a room or directly pass it to the wrapped * {@link IDataStore} if there is no such * * @see org.apache.wicket.pageStore.IDataStore#storeData(java.lang.String, int, byte[]) */ @Override public void storeData(final String sessionId, final int id, final byte[] data) { Entry entry = new Entry(sessionId, id, data); String key = getKey(entry); entryMap.put(key, entry); try { boolean added = entries.offer(entry, OFFER_WAIT, TimeUnit.MILLISECONDS); if (added == false) { log.debug("Storing synchronously page with id '{}' in session '{}'", id, sessionId); entryMap.remove(key); dataStore.storeData(sessionId, id, data); } } catch (InterruptedException e) { log.error(e.getMessage(), e); entryMap.remove(key); dataStore.storeData(sessionId, id, data); } } /** * * @param pageId * @param sessionId * @return generated key */ private static String getKey(final String sessionId, final int pageId) { return pageId + ":::" + sessionId; } /** * * @param entry * @return generated key */ private static String getKey(final Entry entry) { return getKey(entry.sessionId, entry.pageId); } /** * The structure used for an entry in the queue */ private static class Entry { private final String sessionId; private final int pageId; private final byte data[]; public Entry(final String sessionId, final int pageId, final byte data[]) { this.sessionId = Args.notNull(sessionId, "sessionId"); this.pageId = pageId; this.data = Args.notNull(data, "data"); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + pageId; result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Entry other = (Entry)obj; if (pageId != other.pageId) return false; if (sessionId == null) { if (other.sessionId != null) return false; } else if (!sessionId.equals(other.sessionId)) return false; return true; } @Override public String toString() { return "Entry [sessionId=" + sessionId + ", pageId=" + pageId + "]"; } } /** * The thread that acts as consumer of {@link Entry}ies */ private static class PageSavingRunnable implements Runnable { private static final Logger log = LoggerFactory.getLogger(PageSavingRunnable.class); private final BlockingQueue entries; private final ConcurrentMap entryMap; private final IDataStore dataStore; private PageSavingRunnable(IDataStore dataStore, BlockingQueue entries, ConcurrentMap entryMap) { this.dataStore = dataStore; this.entries = entries; this.entryMap = entryMap; } @Override public void run() { while (!Thread.interrupted()) { Entry entry = null; try { entry = entries.poll(POLL_WAIT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } if (entry != null) { log.debug("Saving asynchronously: {}...", entry); dataStore.storeData(entry.sessionId, entry.pageId, entry.data); entryMap.remove(getKey(entry)); } } } } @Override public final boolean canBeAsynchronous() { // should not wrap in abother AsynchronousDataStore return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy