Source Code Cross Referenced for TransactionServlet.java in  » Web-Services » Gomba » org » gomba » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Services » Gomba » org.gomba 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package org.gomba;
002:
003:        import java.io.IOException;
004:        import java.sql.SQLException;
005:        import java.text.SimpleDateFormat;
006:        import java.util.ArrayList;
007:        import java.util.Collections;
008:        import java.util.Date;
009:        import java.util.HashMap;
010:        import java.util.Iterator;
011:        import java.util.List;
012:        import java.util.Map;
013:        import java.util.regex.Matcher;
014:        import java.util.regex.Pattern;
015:
016:        import javax.servlet.ServletConfig;
017:        import javax.servlet.ServletException;
018:        import javax.servlet.http.HttpServlet;
019:        import javax.servlet.http.HttpServletRequest;
020:        import javax.servlet.http.HttpServletResponse;
021:        import javax.sql.DataSource;
022:        import javax.xml.transform.OutputKeys;
023:        import javax.xml.transform.Result;
024:        import javax.xml.transform.Source;
025:        import javax.xml.transform.Transformer;
026:        import javax.xml.transform.TransformerException;
027:        import javax.xml.transform.TransformerFactory;
028:        import javax.xml.transform.sax.SAXSource;
029:        import javax.xml.transform.stream.StreamResult;
030:
031:        import org.gomba.utils.servlet.ServletLogger;
032:        import org.gomba.utils.token.IdGenerator;
033:        import org.gomba.utils.xml.ContentHandlerUtils;
034:        import org.gomba.utils.xml.ObjectInputSource;
035:        import org.gomba.utils.xml.ObjectXMLReader;
036:        import org.xml.sax.InputSource;
037:        import org.xml.sax.SAXException;
038:
039:        /**
040:         * This servlet implements transactions for RESTful web services. This servlet
041:         * is used to start, commit or rollback a transaction. This servlet will be
042:         * typically mapped to <code>/transactions/*</code>.
043:         * 
044:         * HTTP methods are mapped to transaction operations in this way:
045:         * <dl>
046:         * <dt>POST</dt>
047:         * <dd>Start a new transaction. Posting an empty resource will create a new
048:         * transaction. In the future we may add an XML document type to specify
049:         * transaction options.</dd>
050:         * <dt>GET</dt>
051:         * <dd>Get information about a transaction.</dd>
052:         * <dt>PUT</dt>
053:         * <dd>Commit a transaction. Putting an empty resource is allowed. In the
054:         * future we may support PUTting the (updated) XML data returned by GET.</dd>
055:         * <dt>DELETE</dt>
056:         * <dd>Rollback a transaction and destroy it.</dd>
057:         * </dl>
058:         * 
059:         * The XML returned by this servlet in response to GET request looks like this:
060:         * 
061:         * <pre>
062:         *    &lt;transaction&gt;
063:         *     &lt;uri&gt;http://domain.com/transactions/238476245jhg34589345k&lt;/uri&gt;
064:         *     &lt;creationTime&gt;2004-12-25T00:00:00&lt;/creationTime&gt;
065:         *     &lt;lastAccessedTime&gt;2004-12-25T00:10:00&lt;/lastAccessedTime&gt;
066:         *     &lt;maxInactiveInterval&gt;600&lt;/maxInactiveInterval&gt;
067:         *    &lt;/transaction&gt;
068:         * </pre>
069:         * 
070:         * <p>
071:         * TODO: provide a DTD.
072:         * </p>
073:         * 
074:         * Init-params:
075:         * <dl>
076:         * <dt>transaction-id</dt>
077:         * <dd>An expression that evaluates to the transaction id. May contain ${}
078:         * parameters. This will typically be: ${path.0} (Required)</dd>
079:         * <dt>transaction-uri</dt>
080:         * <dd>The transaction URI. This is not a full-blown expression, ${} cannot be
081:         * used here. Only ${transaction.id} will be replaced with actual transaction
082:         * id. A typical setting is: http://domain.org/transactions/${transaction.id}.
083:         * In a sense, this the reverse of the transaction-id expression: transaction-id
084:         * is used to extract an id from a URI, while transaction-URI is used to
085:         * generate a URI from an id. (Required)</dd>
086:         * <dt>transaction-timeout</dt>
087:         * <dd>The transaction timeout interval for all transactions created by this
088:         * servlet. The specified timeout must be expressed in a whole number of
089:         * seconds. Since version 0.8 this value may be exceeded, it depends on when the
090:         * HarvesterThread processes the transaction. The default value is 30.
091:         * (Optional)</dd>
092:         * </dt>
093:         * <dt>jvm-route</dt>
094:         * <dd>Identifier which must be used in load balancing scenarios to enable
095:         * session affinity. The identifier, which must be unique across all servers
096:         * which participate in the cluster, will be appended to the generated
097:         * transaction identifier, therefore allowing the front end proxy to always
098:         * forward requests that belong to a particular transaction to the same
099:         * instance. Value can be an expression evaluated at servlet initialization
100:         * time, so don't use request-related expressions. The "systemProperty"
101:         * parameter domain is particurarly useful for this setting.</dd>
102:         * </dl>
103:         * 
104:         * @author Flavio Tordini
105:         * @version $Id: TransactionServlet.java,v 1.8 2005/12/07 11:19:01 flaviotordini
106:         *          Exp $
107:         * @see http://www.seairth.com/web/resttp.html,
108:         *      http://www.xml.com/lpt/a/2004/08/11/rest.html,
109:         *      http://lists.xml.org/archives/xml-dev/200402/msg00267.html
110:         *      http://groups.yahoo.com/group/rest-discuss/message/4141
111:         */
112:        public class TransactionServlet extends HttpServlet {
113:
114:            /**
115:             * A regular expression used to build the transaction URI.
116:             */
117:            private final static Pattern TRANSACTION_ID_PATTERN = Pattern
118:                    .compile("\\$\\{transaction.id\\}");
119:
120:            /**
121:             * Name of the context attribute that holds the transactions Map. Mapping is
122:             * transaction URI to Trasaction.
123:             */
124:            protected final static String CONTEXT_ATTRIBUTE_NAME_TRANSACTIONS = "org_gomba_transactions";
125:
126:            /**
127:             * Name of the context attribute that holds the servlet instance.
128:             */
129:            protected final static String CONTEXT_ATTRIBUTE_NAME_SERVLET = "org_gomba_transactionServlet";
130:
131:            private final static String INIT_PARAM_TRANSACTION_URI = "transaction-uri";
132:
133:            private final static String INIT_PARAM_TRANSACTION_ID = "transaction-id";
134:
135:            private final static String INIT_PARAM_TRANSACTION_TIMEOUT = "transaction-timeout";
136:
137:            private final static String INIT_PARAM_JVMROUTE = "jvm-route";
138:
139:            private final static int DEFAULT_TRANSACTION_TIMEOUT = 30;
140:
141:            /**
142:             * <code>true</code> if debug logging is turned on.
143:             */
144:            private boolean debugMode;
145:
146:            /**
147:             * A logger to be passed around to enable servlet logging in non-servlet
148:             * classes.
149:             */
150:            private ServletLogger logger;
151:
152:            /**
153:             * The data source to query.
154:             */
155:            private DataSource dataSource;
156:
157:            /**
158:             * Expression that evaluates to the transaction id. Will tipically be the
159:             * first extra path element. The id of a transaction is private affair of
160:             * this servlet. Clients refer to a transaction using its URI.
161:             */
162:            private Expression idExpression;
163:
164:            /**
165:             * Pseudo-expression that evaluates to the transaction URI. This is not a
166:             * full-blown expression, only the <code>TRANSACTION_ID_PATTERN</code> is
167:             * replaced with actual transaction id.
168:             */
169:            private String uriExpression;
170:
171:            /**
172:             * Transaction timeout in seconds
173:             */
174:            private int transactionTimeout = DEFAULT_TRANSACTION_TIMEOUT;
175:
176:            /**
177:             * Map containing current active transactions. Mapping is transaction URI to
178:             * Transaction.
179:             */
180:            private Map transactions;
181:
182:            /**
183:             * This will help us generate secure random transaction ids
184:             */
185:            private final IdGenerator idGenerator = new IdGenerator();
186:
187:            /**
188:             * <code>true</code> when the destroy() method has been called by the
189:             * servlet container.
190:             */
191:            private boolean destroyed;
192:
193:            /**
194:             * Dummy object acting as a semaphore for the harvester thread.
195:             */
196:            private final Object semaphore = new Object();
197:
198:            /**
199:             * Server identifier
200:             */
201:            private String jvmRoute;
202:
203:            /**
204:             * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
205:             */
206:            public void init(ServletConfig config) throws ServletException {
207:                super .init(config);
208:
209:                // debug mode
210:                this .debugMode = AbstractServlet.getDebugMode(config);
211:
212:                // build our strange logger
213:                this .logger = new ServletLogger(this , this .debugMode);
214:
215:                // get the JNDI data source
216:                this .dataSource = AbstractServlet.getDataSource(config);
217:
218:                // transaction URI pseudo-expression
219:                this .uriExpression = config
220:                        .getInitParameter(INIT_PARAM_TRANSACTION_URI);
221:                if (this .uriExpression == null) {
222:                    throw new ServletException("Missing init-param: "
223:                            + INIT_PARAM_TRANSACTION_URI);
224:                }
225:
226:                // transaction id expression
227:                String idExpressionStr = config
228:                        .getInitParameter(INIT_PARAM_TRANSACTION_ID);
229:                if (idExpressionStr == null) {
230:                    throw new ServletException("Missing init-param: "
231:                            + INIT_PARAM_TRANSACTION_ID);
232:                }
233:                try {
234:                    this .idExpression = new Expression(idExpressionStr);
235:                } catch (Exception e) {
236:                    throw new ServletException("Error parsing "
237:                            + INIT_PARAM_TRANSACTION_ID + " expression.", e);
238:                }
239:
240:                // transaction timeout
241:                String transactionTimeoutStr = config
242:                        .getInitParameter(INIT_PARAM_TRANSACTION_TIMEOUT);
243:                if (transactionTimeoutStr != null) {
244:                    this .transactionTimeout = Integer
245:                            .parseInt(transactionTimeoutStr);
246:                }
247:
248:                // server identifier
249:                String jvmRouteStr = config
250:                        .getInitParameter(INIT_PARAM_JVMROUTE);
251:                if (jvmRouteStr != null) {
252:                    Expression jvmRouteExpression;
253:                    try {
254:                        jvmRouteExpression = new Expression(jvmRouteStr);
255:                    } catch (Exception e) {
256:                        throw new ServletException("Error parsing "
257:                                + INIT_PARAM_JVMROUTE + " expression.", e);
258:                    }
259:                    // domains using the request will throw a npe
260:                    ParameterResolver parameterResolver = new ParameterResolver(
261:                            null);
262:                    try {
263:                        this .jvmRoute = jvmRouteExpression.replaceParameters(
264:                                parameterResolver).toString();
265:                    } catch (Exception e) {
266:                        throw new ServletException("Error evaluating "
267:                                + INIT_PARAM_JVMROUTE + " expression.", e);
268:                    }
269:                }
270:
271:                // put this servlet instance in application scope
272:                // FIXME servlets shold not be put in scopes
273:                if (getServletContext().getAttribute(
274:                        CONTEXT_ATTRIBUTE_NAME_SERVLET) != null) {
275:                    throw new ServletException("A " + this .getClass().getName()
276:                            + " is already configured for the current context.");
277:                }
278:                getServletContext().setAttribute(
279:                        CONTEXT_ATTRIBUTE_NAME_SERVLET, this );
280:
281:                // init the transactions map
282:                // since we're in a servlet env where multiple thread will modify the
283:                // map, we need a synchronized impl
284:                this .transactions = Collections.synchronizedMap(new HashMap());
285:                getServletContext().setAttribute(
286:                        CONTEXT_ATTRIBUTE_NAME_TRANSACTIONS, this .transactions);
287:
288:                // start background thread that removes expired transactions
289:                TransactionHarvester harvester = new TransactionHarvester();
290:                harvester.setDaemon(true);
291:                harvester.setPriority(Thread.MIN_PRIORITY);
292:                harvester.start();
293:
294:            }
295:
296:            /**
297:             * Create a new transaction and add it the map.
298:             */
299:            protected Transaction createTransaction() throws ServletException {
300:
301:                final Transaction transaction;
302:
303:                // build transaction URI
304:                // since this block is not synchronized
305:                // there is a slight chance to generate an existing id
306:                String transactionURI;
307:                do {
308:                    // generate id
309:                    String transactionId = this .idGenerator.generateId();
310:                    if (this .jvmRoute != null) {
311:                        transactionId += '.' + this .jvmRoute;
312:                    }
313:                    transactionURI = getTransactionURI(transactionId);
314:                } while (this .transactions.containsKey(transactionURI));
315:
316:                // create transaction
317:                transaction = new Transaction(this .logger, this .dataSource,
318:                        transactionURI, this .transactionTimeout);
319:
320:                // add transaction to our map
321:                this .transactions.put(transaction.getUri(), transaction);
322:
323:                this .logger.debug("Created transaction: "
324:                        + transaction.getUri());
325:
326:                return transaction;
327:            }
328:
329:            /**
330:             * Create a transaction.
331:             * 
332:             * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
333:             *           javax.servlet.http.HttpServletResponse)
334:             */
335:            protected void doPost(HttpServletRequest request,
336:                    HttpServletResponse response) throws ServletException,
337:                    IOException {
338:
339:                // TODO do we need XML from the request body ???
340:                // TODO if we need it, it should be customizable with XSLT
341:
342:                // create the transaction
343:                Transaction transaction = createTransaction();
344:
345:                // 201!
346:                response.setStatus(HttpServletResponse.SC_CREATED);
347:
348:                // set Location header
349:                response.setHeader("Location", transaction.getUri());
350:
351:                // TODO output XML with XLink
352:                // TODO and make it customizable via XSLT
353:
354:            }
355:
356:            /**
357:             * Get the transaction id by evaluating the id expression.
358:             * 
359:             * @return Never returns null
360:             */
361:            private String getTransactionId(ParameterResolver parameterResolver)
362:                    throws ServletException {
363:
364:                Object obj;
365:                try {
366:                    obj = this .idExpression
367:                            .replaceParameters(parameterResolver);
368:                } catch (Exception e) {
369:                    throw new ServletException(
370:                            "Error evaluating transaction id expression.", e);
371:                }
372:
373:                if (obj instanceof  java.lang.String) {
374:                    return (String) obj;
375:                }
376:
377:                throw new ServletException(
378:                        "Transaction id expression does not evaluate to String: "
379:                                + obj.getClass().getName());
380:
381:            }
382:
383:            /**
384:             * Get the transaction URI by evaluating the URI pseudo expression.
385:             */
386:            private String getTransactionURI(String transactionId) {
387:                Matcher m = TRANSACTION_ID_PATTERN.matcher(this .uriExpression);
388:                return m.replaceFirst(transactionId);
389:            }
390:
391:            /**
392:             * Get the Transaction object for the specified request.
393:             * 
394:             * @return May return null if the specified transaction does not exist.
395:             */
396:            private Transaction getTransaction(HttpServletRequest request)
397:                    throws ServletException {
398:
399:                // create the parameter resolver that will help us throughout this
400:                // request
401:                final ParameterResolver parameterResolver = new ParameterResolver(
402:                        request);
403:
404:                // get transaction id
405:                String transactionId = getTransactionId(parameterResolver);
406:
407:                // get transaction URI
408:                String transactionURI = getTransactionURI(transactionId);
409:
410:                // get transaction
411:                Transaction transaction = (Transaction) this .transactions
412:                        .get(transactionURI);
413:
414:                if (transaction == null) {
415:                    log("Invalid or expired transaction: " + transactionURI);
416:                }
417:
418:                return transaction;
419:
420:            }
421:
422:            /**
423:             * Get the Transaction object for the specified request.
424:             * 
425:             * @return May return null if the specified transaction does not exist.
426:             */
427:            private Transaction getAndRemoveTransaction(
428:                    HttpServletRequest request) throws ServletException {
429:
430:                // create the parameter resolver that will help us throughout this
431:                // request
432:                final ParameterResolver parameterResolver = new ParameterResolver(
433:                        request);
434:
435:                // get transaction id
436:                String transactionId = getTransactionId(parameterResolver);
437:
438:                // get transaction URI
439:                String transactionURI = getTransactionURI(transactionId);
440:
441:                // get transaction
442:                Transaction transaction = (Transaction) this .transactions
443:                        .remove(transactionURI);
444:
445:                if (transaction == null) {
446:                    log("Invalid, expired or completed transaction: "
447:                            + transactionURI);
448:                }
449:
450:                return transaction;
451:
452:            }
453:
454:            /**
455:             * Obtain information about a transaction.
456:             * 
457:             * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
458:             *           javax.servlet.http.HttpServletResponse)
459:             */
460:            protected void doGet(HttpServletRequest request,
461:                    HttpServletResponse response) throws ServletException,
462:                    IOException {
463:
464:                // get transaction
465:                Transaction transaction = getTransaction(request);
466:                if (transaction == null) {
467:                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
468:                    return;
469:                }
470:
471:                // output xml
472:                try {
473:                    serializeXML(transaction, response);
474:                } catch (Exception e) {
475:                    throw new ServletException(
476:                            "Error serializing transaction to XML.", e);
477:                }
478:
479:            }
480:
481:            /**
482:             * Remove a transaction from the map.
483:             */
484:            private void removeTransaction(Transaction transaction)
485:                    throws ServletException {
486:                Object previousValue = this .transactions.remove(transaction
487:                        .getUri());
488:                if (previousValue == null) {
489:                    log("Transaction already removed: " + transaction.getUri());
490:                }
491:            }
492:
493:            /**
494:             * Serialize an object to XML using SAX and TrAX APIs in a smart way.
495:             * (dagnele sucks :)
496:             * 
497:             * @param object
498:             *                   The object to serialize
499:             * @param saxReader
500:             *                   The SAX "parser"
501:             * @param response
502:             *                   The HTTP response
503:             * @see <a
504:             *           href="http://java.sun.com/j2se/1.4.2/docs/api/javax/xml/transform/package-summary.html">TrAX
505:             *           API </a>
506:             */
507:            private void serializeXML(Transaction object,
508:                    HttpServletResponse response) throws TransformerException,
509:                    IOException {
510:
511:                ObjectXMLReader saxReader = new TransactionXMLReader();
512:
513:                // Let the HTTP client know the output content type
514:                response.setContentType("text/xml");
515:
516:                // Create TrAX Transformer
517:                Transformer t = TransformerFactory.newInstance()
518:                        .newTransformer();
519:
520:                // Set trasformation output properties
521:                t.setOutputProperty(OutputKeys.ENCODING, response
522:                        .getCharacterEncoding());
523:
524:                // Create the trasformation source using our custom ObjectInputSource
525:                InputSource inputSource = new ObjectInputSource(object);
526:                Source source = new SAXSource(saxReader, inputSource);
527:
528:                // Create the trasformation result
529:                // Result result = new StreamResult(response.getWriter());
530:                Result result = new StreamResult(response.getOutputStream());
531:
532:                // Go!
533:                t.transform(source, result);
534:
535:            }
536:
537:            /**
538:             * Commit a transaction.
539:             * 
540:             * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
541:             *      javax.servlet.http.HttpServletResponse)
542:             */
543:            protected void doPut(HttpServletRequest request,
544:                    HttpServletResponse response) throws ServletException,
545:                    IOException {
546:
547:                // get transaction
548:                Transaction transaction = getAndRemoveTransaction(request);
549:                if (transaction == null) {
550:                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
551:                    return;
552:                }
553:
554:                // commit transaction
555:                this .logger.debug("Committing transaction: "
556:                        + transaction.getUri());
557:                try {
558:                    transaction.commit();
559:                } catch (SQLException e) {
560:                    throw new ServletException("Error committing transaction: "
561:                            + transaction.getUri(), e);
562:                }
563:
564:                // output xml
565:                try {
566:                    serializeXML(transaction, response);
567:                } catch (Exception e) {
568:                    throw new ServletException(
569:                            "Error serializing transaction to XML.", e);
570:                }
571:
572:            }
573:
574:            /**
575:             * Rollback a transaction.
576:             * 
577:             * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest,
578:             *      javax.servlet.http.HttpServletResponse)
579:             */
580:            protected void doDelete(HttpServletRequest request,
581:                    HttpServletResponse response) throws ServletException,
582:                    IOException {
583:
584:                // get transaction
585:                Transaction transaction = getAndRemoveTransaction(request);
586:                if (transaction == null) {
587:                    response.sendError(HttpServletResponse.SC_NOT_FOUND);
588:                    return;
589:                }
590:
591:                // rollback transaction
592:                this .logger.debug("Rolling back transaction: "
593:                        + transaction.getUri());
594:                try {
595:                    transaction.rollback();
596:                } catch (SQLException e) {
597:                    throw new ServletException(
598:                            "Error rolling back transaction: "
599:                                    + transaction.getUri(), e);
600:                }
601:
602:            }
603:
604:            /**
605:             * @see javax.servlet.Servlet#destroy()
606:             */
607:            public void destroy() {
608:
609:                // check for multiple calls
610:                if (this .destroyed) {
611:                    log("Servlet already destroyed. This should not happen.");
612:                    return;
613:                }
614:
615:                // mark this servlet as destroyed so the harvester thread knows it
616:                // has to exit.
617:                this .destroyed = true;
618:
619:                // notify the harvester thread
620:                synchronized (this .semaphore) {
621:                    this .semaphore.notifyAll();
622:                }
623:            }
624:
625:            /**
626:             * This SAX XMLReader generates an XML document from a Transaction.
627:             */
628:            final class TransactionXMLReader extends ObjectXMLReader {
629:
630:                private static final String PATTERN_TIMESTAMP = "yyyy-MM-dd'T'HH:mm:ss";
631:
632:                private final static String ROOT_ELEMENT = "transaction";
633:
634:                private final static String ELEMENT_URI = "uri";
635:
636:                private final static String ELEMENT_CREATIONTIME = "creationTime";
637:
638:                private final static String ELEMENT_LASTACCESSED = "lastAccessedTime";
639:
640:                private final static String ELEMENT_MAXINACTIVEINTERVAL = "maxInactiveInterval";
641:
642:                /**
643:                 * @see org.gomba.utils.xml.ObjectXMLReader#parse(org.gomba.utils.xml.ObjectInputSource)
644:                 */
645:                public void parse(ObjectInputSource input) throws IOException,
646:                        SAXException {
647:
648:                    // Note that SimpleDateFormat objects cannot be used
649:                    // concurrently by multiple threads
650:                    SimpleDateFormat timestampFormatter = new SimpleDateFormat(
651:                            PATTERN_TIMESTAMP);
652:
653:                    Transaction transaction = (Transaction) input.getObject();
654:
655:                    this .handler.startDocument();
656:                    this .handler.startElement(ContentHandlerUtils.DUMMY_NSU,
657:                            ROOT_ELEMENT, ROOT_ELEMENT,
658:                            ContentHandlerUtils.DUMMY_ATTS);
659:
660:                    ContentHandlerUtils.tag(this .handler, ELEMENT_URI,
661:                            transaction.getUri());
662:
663:                    ContentHandlerUtils.tag(this .handler, ELEMENT_CREATIONTIME,
664:                            timestampFormatter.format(transaction
665:                                    .getCreationTime()));
666:
667:                    ContentHandlerUtils.tag(this .handler, ELEMENT_LASTACCESSED,
668:                            timestampFormatter.format(new Date(transaction
669:                                    .getLastAccessedTime())));
670:
671:                    ContentHandlerUtils.tag(this .handler,
672:                            ELEMENT_MAXINACTIVEINTERVAL, Integer
673:                                    .toString(transaction
674:                                            .getMaxInactiveInterval()));
675:
676:                    this .handler.endElement(ContentHandlerUtils.DUMMY_NSU,
677:                            ROOT_ELEMENT, ROOT_ELEMENT);
678:                    this .handler.endDocument();
679:
680:                }
681:
682:            }
683:
684:            private class TransactionHarvester extends Thread {
685:
686:                /**
687:                 * Private constructor
688:                 */
689:                private TransactionHarvester() {
690:                    // Give this thread a useful name
691:                    super ("TransactionHarvester");
692:                }
693:
694:                public void run() {
695:
696:                    log(this  + " started.");
697:
698:                    do {
699:
700:                        try {
701:
702:                            // create a temporary list of transactions
703:                            // to avoid synchronization problems
704:                            final List transactionsList = new ArrayList(
705:                                    transactions.keySet());
706:
707:                            // scan for expired transactions
708:                            for (Iterator i = transactionsList.iterator(); i
709:                                    .hasNext();) {
710:                                final String transactionUri = (String) i.next();
711:                                final Transaction transaction = (Transaction) transactions
712:                                        .get(transactionUri);
713:                                if (transaction == null) {
714:                                    // transaction has been committed or rolled back in the meantime...
715:                                    continue;
716:                                }
717:                                if (transaction.isExpired()) {
718:                                    log("Found expired transaction: "
719:                                            + transaction.getUri());
720:                                    try {
721:                                        removeTransaction(transaction);
722:                                    } catch (Exception e) {
723:                                        log(
724:                                                "Error removing expired transaction: "
725:                                                        + transaction.getUri(),
726:                                                e);
727:                                    } finally {
728:                                        try {
729:                                            transaction.rollback();
730:                                        } catch (Exception e) {
731:                                            log(
732:                                                    "Error rolling back expired transaction: "
733:                                                            + transaction
734:                                                                    .getUri(),
735:                                                    e);
736:                                        }
737:                                    }
738:                                }
739:                            }
740:
741:                            // sleep interval or until notified
742:                            synchronized (TransactionServlet.this .semaphore) {
743:                                try {
744:                                    TransactionServlet.this .semaphore
745:                                            .wait(transactionTimeout * 1000);
746:                                } catch (InterruptedException e) {
747:                                    log("Thread interrupted: "
748:                                            + Thread.currentThread(), e);
749:                                }
750:                            }
751:
752:                        } catch (Throwable t) {
753:                            // this prevents the thread from dying for an uncaught
754:                            // exception
755:                            log("Error checking for expired transactions.", t);
756:                        }
757:
758:                    } while (!TransactionServlet.this .destroyed);
759:
760:                    log(this  + " stopped.");
761:                }
762:
763:            }
764:
765:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.