001: /******************************************************************************
002: * JBoss, a division of Red Hat *
003: * Copyright 2006, Red Hat Middleware, LLC, and individual *
004: * contributors as indicated by the @authors tag. See the *
005: * copyright.txt in the distribution for a full listing of *
006: * individual contributors. *
007: * *
008: * This is free software; you can redistribute it and/or modify it *
009: * under the terms of the GNU Lesser General Public License as *
010: * published by the Free Software Foundation; either version 2.1 of *
011: * the License, or (at your option) any later version. *
012: * *
013: * This software is distributed in the hope that it will be useful, *
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of *
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
016: * Lesser General Public License for more details. *
017: * *
018: * You should have received a copy of the GNU Lesser General Public *
019: * License along with this software; if not, write to the Free *
020: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA *
021: * 02110-1301 USA, or see the FSF site: http://www.fsf.org. *
022: ******************************************************************************/package org.jboss.portal.widget;
023:
024: import java.net.MalformedURLException;
025: import java.net.URL;
026: import java.util.concurrent.ConcurrentHashMap;
027: import java.util.concurrent.ConcurrentMap;
028: import java.util.concurrent.ExecutorService;
029: import java.util.concurrent.LinkedBlockingQueue;
030: import java.util.concurrent.ScheduledExecutorService;
031: import java.util.concurrent.ScheduledThreadPoolExecutor;
032: import java.util.concurrent.ThreadPoolExecutor;
033: import java.util.concurrent.TimeUnit;
034:
035: import org.jboss.portal.widget.exceptions.DirectoryResultFailure;
036: import org.jboss.portal.widget.exceptions.WidgetFailure;
037:
038: /**
039: * @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
040: * @version $Revision$
041: */
042: public abstract class AbstractWidgetProvider implements WidgetProvider {
043:
044: /** The widget entry map */
045: protected ConcurrentMap<URL, ExpiringFutureTask<Widget>> entries = new ConcurrentHashMap<URL, ExpiringFutureTask<Widget>>();
046:
047: /** The query result map */
048: protected ConcurrentMap<WidgetQuery, ExpiringFutureTask<DirectoryQueryResult>> queries = new ConcurrentHashMap<WidgetQuery, ExpiringFutureTask<DirectoryQueryResult>>();
049:
050: /** The executor */
051: protected ExecutorService executor;
052:
053: /** The scheduled executor */
054: protected ScheduledExecutorService scheduledExecutor;
055:
056: /** The entry expiration time in millis (default: 6 hours) */
057: protected long entryExpiration = TimeUnit.MILLISECONDS.convert(
058: 21600, TimeUnit.SECONDS);
059:
060: /** The query expiration time in millis (default: 1 hour) */
061: protected long queryExpiration = TimeUnit.MILLISECONDS.convert(
062: 3600, TimeUnit.SECONDS);
063:
064: /** Fetch all widgets in on the directory lookup */
065: private boolean fetchWidgetsOnDirectoryLookup = false;
066:
067: /** The connection timeout */
068: protected int connectionTimeout = 5000;
069:
070: /** Eviction thread timing */
071: protected long timing = TimeUnit.MILLISECONDS.convert(60,
072: TimeUnit.SECONDS);
073:
074: /** The logger */
075: private static final org.jboss.logging.Logger log = org.jboss.logging.Logger
076: .getLogger(WidgetProvider.class);
077:
078: public void start() {
079: executor = new ThreadPoolExecutor(4, 6, 0, TimeUnit.SECONDS,
080: new LinkedBlockingQueue());
081: scheduledExecutor = new ScheduledThreadPoolExecutor(2);
082:
083: scheduledExecutor.scheduleAtFixedRate(
084: new EntryEvictionThread(), timing, timing,
085: TimeUnit.MILLISECONDS);
086: scheduledExecutor.scheduleAtFixedRate(
087: new QueryEvictionThread(), timing, timing,
088: TimeUnit.MILLISECONDS);
089: }
090:
091: public void stop() {
092: executor.shutdownNow();
093: scheduledExecutor.shutdownNow();
094: executor = null;
095: scheduledExecutor = null;
096: }
097:
098: public long getConnectionTimeout() {
099: return connectionTimeout;
100: }
101:
102: public void setConnectionTimeout(int connectionTimeout) {
103: this .connectionTimeout = connectionTimeout;
104: }
105:
106: public void setQueryExpiration(long millis) {
107: this .queryExpiration = millis;
108: }
109:
110: public void setEntryExpiration(long millis) {
111: this .entryExpiration = millis;
112: }
113:
114: public void setFetchWidgetsOnDirectoryLookup(boolean fetch) {
115: this .fetchWidgetsOnDirectoryLookup = fetch;
116: }
117:
118: /**
119: * Abstract method for adding a directory query and submitting to the executor
120: *
121: * @param query
122: */
123: protected abstract void addDirectoryResult(WidgetQuery query);
124:
125: /**
126: * Abstract method for adding a widget url and submitting it to the executor
127: *
128: * @param url
129: */
130: protected abstract void addWidget(URL url);
131:
132: /**
133: * Get widget (instance of WidgetFailure on failure)
134: *
135: * @param url
136: * @return Widget
137: */
138: public Widget getWidget(URL url) {
139: addWidget(url);
140: ExpiringFutureTask<Widget> entry = entries.get(url);
141: try {
142: return entry.get();
143: } catch (Exception e) {
144: log.debug("Failed to get widget.", e.getCause());
145: entries.remove(entry);
146: return new WidgetFailure(url, e);
147: }
148: }
149:
150: public Widget getWidget(String string) {
151: try {
152: URL url = new URL(string);
153: // Get widget
154: return getWidget(url);
155: } catch (MalformedURLException e) {
156: log.debug("Failed to create widget url out of String: "
157: + string);
158: return new WidgetFailure(null, e);
159: }
160: }
161:
162: private DirectoryQueryResult getDirectoryResult(WidgetQuery query) {
163: addDirectoryResult(query);
164: ExpiringFutureTask<DirectoryQueryResult> entry = queries
165: .get(query);
166: try {
167: return entry.get();
168: } catch (Exception e) {
169: log.debug(
170: "Failed to retreive widget directory information",
171: e);
172: queries.remove(query);
173: return new DirectoryResultFailure(e);
174: }
175: }
176:
177: public DirectoryQueryResult search(WidgetQuery q) {
178: if (q == null) {
179: return new DirectoryResultFailure(
180: new IllegalArgumentException(
181: "Query can not be null"));
182: }
183:
184: //
185: DirectoryQueryResult result = getDirectoryResult(q);
186:
187: if (this .fetchWidgetsOnDirectoryLookup) {
188: for (URL url : result.collection()) {
189: // add widgets and submit to executor if necessary
190: addWidget(url);
191: }
192: }
193:
194: return result;
195: }
196:
197: private class EntryEvictionThread implements Runnable {
198: public void run() {
199: if (entries != null && entries.size() > 0) {
200: for (URL url : entries.keySet()) {
201: ExpiringFutureTask<Widget> w = entries.get(url);
202: if (w.isExpired()) {
203: entries.remove(url);
204: }
205: }
206: }
207: }
208: }
209:
210: private class QueryEvictionThread implements Runnable {
211: public void run() {
212: if (queries != null && queries.size() > 0) {
213: for (WidgetQuery query : queries.keySet()) {
214: ExpiringFutureTask<DirectoryQueryResult> e = queries
215: .get(query);
216: if (e.isExpired()) {
217: queries.remove(query);
218: }
219: }
220: }
221: }
222: }
223:
224: }
|