View Javadoc

1   /**
2      This file is part of GoldenGate Project (named also GoldenGate or GG).
3   
4      Copyright 2009, Frederic Bregier, and individual contributors by the @author
5      tags. See the COPYRIGHT.txt in the distribution for a full listing of
6      individual contributors.
7   
8      All GoldenGate Project is free software: you can redistribute it and/or 
9      modify it under the terms of the GNU General Public License as published 
10     by the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12  
13     GoldenGate is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17  
18     You should have received a copy of the GNU General Public License
19     along with GoldenGate .  If not, see <http://www.gnu.org/licenses/>.
20   */
21  package openr66.protocol.http;
22  
23  import goldengate.common.database.DbAdmin;
24  import goldengate.common.database.DbPreparedStatement;
25  import goldengate.common.database.DbSession;
26  import goldengate.common.database.data.AbstractDbData.UpdatedInfo;
27  import goldengate.common.database.exception.GoldenGateDatabaseException;
28  import goldengate.common.database.exception.GoldenGateDatabaseNoConnectionException;
29  import goldengate.common.database.exception.GoldenGateDatabaseSqlException;
30  import goldengate.common.exception.FileTransferException;
31  import goldengate.common.exception.InvalidArgumentException;
32  import goldengate.common.logging.GgInternalLogger;
33  import goldengate.common.logging.GgInternalLoggerFactory;
34  import goldengate.common.utility.GgStringUtils;
35  
36  import java.io.IOException;
37  import java.util.Date;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Set;
41  import java.util.concurrent.ConcurrentHashMap;
42  
43  import openr66.context.ErrorCode;
44  import openr66.context.R66Session;
45  import openr66.context.filesystem.R66Dir;
46  import openr66.database.DbConstant;
47  import openr66.database.data.DbTaskRunner;
48  import openr66.database.data.DbTaskRunner.TASKSTEP;
49  import openr66.protocol.configuration.Configuration;
50  import openr66.protocol.exception.OpenR66Exception;
51  import openr66.protocol.exception.OpenR66ExceptionTrappedFactory;
52  import openr66.protocol.exception.OpenR66ProtocolBusinessNoWriteBackException;
53  import openr66.protocol.localhandler.LocalChannelReference;
54  
55  import org.jboss.netty.buffer.ChannelBuffer;
56  import org.jboss.netty.buffer.ChannelBuffers;
57  import org.jboss.netty.channel.ChannelFuture;
58  import org.jboss.netty.channel.ChannelFutureListener;
59  import org.jboss.netty.channel.ChannelHandlerContext;
60  import org.jboss.netty.channel.ChannelStateEvent;
61  import org.jboss.netty.channel.ExceptionEvent;
62  import org.jboss.netty.channel.MessageEvent;
63  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
64  import org.jboss.netty.channel.group.ChannelGroup;
65  import org.jboss.netty.handler.codec.http.Cookie;
66  import org.jboss.netty.handler.codec.http.CookieDecoder;
67  import org.jboss.netty.handler.codec.http.CookieEncoder;
68  import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
69  import org.jboss.netty.handler.codec.http.HttpHeaders;
70  import org.jboss.netty.handler.codec.http.HttpMethod;
71  import org.jboss.netty.handler.codec.http.HttpRequest;
72  import org.jboss.netty.handler.codec.http.HttpResponse;
73  import org.jboss.netty.handler.codec.http.HttpResponseStatus;
74  import org.jboss.netty.handler.codec.http.HttpVersion;
75  import org.jboss.netty.handler.codec.http.QueryStringDecoder;
76  import org.jboss.netty.handler.traffic.TrafficCounter;
77  
78  /**
79   * Handler for HTTP information support
80   *
81   * @author Frederic Bregier
82   *
83   */
84  public class HttpFormattedHandler extends SimpleChannelUpstreamHandler {
85      /**
86       * Internal Logger
87       */
88      private static final GgInternalLogger logger = GgInternalLoggerFactory
89              .getLogger(HttpFormattedHandler.class);
90  
91      private static enum REQUEST {
92          index("index.html"),
93          active("monitoring_header.html","monitoring_end.html"), 
94          error("monitoring_header.html","monitoring_end.html"), 
95          done("monitoring_header.html","monitoring_end.html"), 
96          all("monitoring_header.html","monitoring_end.html"), 
97          status("monitoring_header.html","monitoring_end.html"),
98          statusxml("");
99          
100         private String header;
101         private String end;
102         /**
103          * Constructor for a unique file
104          * @param uniquefile
105          */
106         private REQUEST(String uniquefile) {
107             this.header = uniquefile;
108             this.end = uniquefile;
109         }
110         /**
111          * @param header
112          * @param end
113          */
114         private REQUEST(String header, String end) {
115             this.header = header;
116             this.end = end;
117         }
118         
119         /**
120          * Reader for a unique file
121          * @return the content of the unique file
122          */
123         public String readFileUnique(HttpFormattedHandler handler) {
124             return handler.readFileHeader(Configuration.configuration.httpBasePath+"monitor/"+this.header);
125         }
126         
127         public String readHeader(HttpFormattedHandler handler) {
128             return handler.readFileHeader(Configuration.configuration.httpBasePath+"monitor/"+this.header);
129         }
130         public String readEnd() {
131             return GgStringUtils.readFile(Configuration.configuration.httpBasePath+"monitor/"+this.end);
132         }
133     }
134     
135     private static enum REPLACEMENT {
136         XXXHOSTIDXXX, XXXLOCACTIVEXXX, XXXNETACTIVEXXX, XXXBANDWIDTHXXX, XXXDATEXXX;
137     }
138     public static final int LIMITROW = 60;// better if it can be divided by 4
139 
140     public final R66Session authentHttp = new R66Session();
141 
142     public static final ConcurrentHashMap<String, R66Dir> usedDir = new ConcurrentHashMap<String, R66Dir>();
143 
144     private volatile HttpRequest request;
145 
146     private final StringBuilder responseContent = new StringBuilder();
147 
148     private volatile HttpResponseStatus status;
149 
150     private volatile String uriRequest;
151 
152     private static final String sINFO = "INFO", sNB = "NB", sDETAIL = "DETAIL";
153 
154     /**
155      * The Database connection attached to this NetworkChannel shared among all
156      * associated LocalChannels
157      */
158     private DbSession dbSession;
159 
160     /**
161      * Does this dbSession is private and so should be closed
162      */
163     private boolean isPrivateDbSession = false;
164     private boolean isCurrentRequestXml = false;
165     
166     private Map<String, List<String>> params = null;
167 
168     private String readFileHeader(String filename) {
169         String value;
170         try {
171             value = GgStringUtils.readFileException(filename);
172         } catch (InvalidArgumentException e) {
173             logger.error("Error while trying to open: "+filename,e);
174             return "";
175         } catch (FileTransferException e) {
176             logger.error("Error while trying to read: "+filename,e);
177             return "";
178         }
179         StringBuilder builder = new StringBuilder(value);
180         
181         GgStringUtils.replace(builder, REPLACEMENT.XXXDATEXXX.toString(),
182                 (new Date()).toString());
183         GgStringUtils.replace(builder, REPLACEMENT.XXXLOCACTIVEXXX.toString(),
184                 Integer.toString(
185                         Configuration.configuration.getLocalTransaction().
186                         getNumberLocalChannel()));
187         GgStringUtils.replace(builder, REPLACEMENT.XXXNETACTIVEXXX.toString(),
188                 Integer.toString(
189                         DbAdmin.getNbConnection()));
190         GgStringUtils.replace(builder, REPLACEMENT.XXXHOSTIDXXX.toString(),
191                 Configuration.configuration.HOST_ID);
192         TrafficCounter trafficCounter =
193             Configuration.configuration.getGlobalTrafficShapingHandler().getTrafficCounter();
194         GgStringUtils.replace(builder, REPLACEMENT.XXXBANDWIDTHXXX.toString(),
195                 "IN:"+(trafficCounter.getLastReadThroughput()/131072)+
196                 "Mbits&nbsp;&nbsp;OUT:"+
197                 (trafficCounter.getLastWriteThroughput()/131072)+"Mbits");
198         return builder.toString();
199     }
200 
201     private String getTrimValue(String varname) {
202         String value = null;
203         try {
204             value = params.get(varname).get(0).trim();
205         } catch (NullPointerException e) {
206             return null;
207         }
208         if (value.length() == 0) {
209             value = null;
210         }
211         return value;
212     }
213 
214     @Override
215     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
216             throws Exception {
217         isCurrentRequestXml = false;
218         status = HttpResponseStatus.OK;
219         try {
220             if (DbConstant.admin.isConnected) {
221                 this.dbSession = new DbSession(DbConstant.admin, false);
222                 DbAdmin.nbHttpSession++;
223                 this.isPrivateDbSession = true;
224             }
225         } catch (GoldenGateDatabaseNoConnectionException e1) {
226             // Cannot connect so use default connection
227             logger.warn("Use default database connection");
228             this.dbSession = DbConstant.admin.session;
229         }
230         HttpRequest request = this.request = (HttpRequest) e.getMessage();
231         QueryStringDecoder queryStringDecoder = new QueryStringDecoder(request
232                 .getUri());
233         uriRequest = queryStringDecoder.getPath();
234         logger.debug("Msg: "+uriRequest);
235         if (uriRequest.contains("gre/") || uriRequest.contains("img/") ||
236                 uriRequest.contains("res/")) {
237             HttpWriteCacheEnable.writeFile(request, 
238                     e.getChannel(), Configuration.configuration.httpBasePath+uriRequest,
239                     "XYZR66NOSESSION");
240             return;
241         }
242         char cval = 'z';
243         long nb = LIMITROW;
244         // check the URI
245         if (uriRequest.equalsIgnoreCase("/active")) {
246             cval = '0';
247         } else if (uriRequest.equalsIgnoreCase("/error")) {
248             cval = '1';
249         } else if (uriRequest.equalsIgnoreCase("/done")) {
250             cval = '2';
251         } else if (uriRequest.equalsIgnoreCase("/all")) {
252             cval = '3';
253         } else if (uriRequest.equalsIgnoreCase("/status")) {
254             cval = '4';
255         } else if (uriRequest.equalsIgnoreCase("/statusxml")) {
256             cval = '5';
257             nb = 0; // since it could be the default or setup by request
258             isCurrentRequestXml = true;
259         }
260         // Get the params according to get or post
261         if (request.getMethod() == HttpMethod.GET) {
262             params = queryStringDecoder.getParameters();
263         } else if (request.getMethod() == HttpMethod.POST) {
264             ChannelBuffer content = request.getContent();
265             if (content.readable()) {
266                 String param = content.toString(GgStringUtils.UTF8);
267                 queryStringDecoder = new QueryStringDecoder("/?" + param);
268             } else {
269                 responseContent.append(REQUEST.index.readFileUnique(this));
270                 writeResponse(e);
271                 return;
272             }
273             params = queryStringDecoder.getParameters();
274         }
275         boolean getMenu = (cval == 'z');
276         boolean extraBoolean = false;
277         if (!params.isEmpty()) {
278             // if not uri, from get or post
279             if (getMenu) {
280                 String info = getTrimValue(sINFO);
281                 if (info != null) {
282                     getMenu = false;
283                     cval = info.charAt(0);
284                 } else {
285                     getMenu = true;
286                 }
287             }
288             // search the nb param
289             String snb = getTrimValue(sNB);
290             if (snb != null) {
291                 nb = Long.parseLong(snb);
292             }
293             // search the detail param
294             String sdetail = getTrimValue(sDETAIL);
295             if (sdetail != null) {
296                 if (Integer.parseInt(sdetail) > 0)
297                     extraBoolean = true;
298             }
299         }
300         if (getMenu) {
301             responseContent.append(REQUEST.index.readFileUnique(this));
302         } else {
303             // Use value 0=Active 1=Error 2=Done 3=All
304             switch (cval) {
305                 case '0':
306                     active(ctx, (int)nb);
307                     break;
308                 case '1':
309                     error(ctx, (int)nb);
310                     break;
311                 case '2':
312                     done(ctx, (int)nb);
313                     break;
314                 case '3':
315                     all(ctx, (int)nb);
316                     break;
317                 case '4':
318                     status(ctx, (int)nb);
319                     break;
320                 case '5':
321                     statusxml(ctx, nb, extraBoolean);
322                     break;
323                 default:
324                     responseContent.append(REQUEST.index.readFileUnique(this));
325             }
326         }
327         writeResponse(e);
328     }
329 
330     /**
331      * Add all runners from preparedStatement for type
332      *
333      * @param preparedStatement
334      * @param type
335      * @param nb
336      * @throws GoldenGateDatabaseNoConnectionException
337      * @throws GoldenGateDatabaseSqlException
338      */
339     private void addRunners(DbPreparedStatement preparedStatement, String type,
340             int nb) throws GoldenGateDatabaseNoConnectionException,
341             GoldenGateDatabaseSqlException {
342         try {
343             preparedStatement.executeQuery();
344             responseContent
345                     .append("<style>td{font-size: 8pt;}</style><table border=\"2\">");
346             responseContent.append("<tr><td>");
347             responseContent.append(type);
348             responseContent.append("</td>");
349             responseContent.append(DbTaskRunner.headerHtml());
350             responseContent.append("</tr>\r\n");
351             int i = 0;
352             while (preparedStatement.getNext()) {
353                 DbTaskRunner taskRunner = DbTaskRunner
354                         .getFromStatement(preparedStatement);
355                 responseContent.append("<tr><td>");
356                 responseContent.append(taskRunner.isSender()? "S" : "R");
357                 responseContent.append("</td>");
358                 LocalChannelReference lcr =
359                     Configuration.configuration.getLocalTransaction().
360                     getFromRequest(taskRunner.getKey());
361                 responseContent.append(taskRunner.toHtml(authentHttp,
362                         lcr != null ? "Active" : "NotActive"));
363                 responseContent.append("</tr>\r\n");
364                 if (nb > 0) {
365                     i ++;
366                     if (i >= nb) {
367                         break;
368                     }
369                 }
370             }
371             responseContent.append("</table><br>\r\n");
372         } finally {
373             if (preparedStatement != null) {
374                 preparedStatement.realClose();
375             }
376         }
377     }
378 
379     /**
380      * print all active transfers
381      *
382      * @param ctx
383      * @param nb
384      */
385     private void active(ChannelHandlerContext ctx, int nb) {
386         responseContent.append(REQUEST.active.readHeader(this));
387         DbPreparedStatement preparedStatement = null;
388         try {
389             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
390                     ErrorCode.Running, nb);
391             addRunners(preparedStatement, ErrorCode.Running.mesg, nb);
392             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
393                     dbSession, UpdatedInfo.INTERRUPTED, true, nb);
394             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
395             addRunners(preparedStatement, UpdatedInfo.INTERRUPTED.name(), nb);
396             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
397                     dbSession, UpdatedInfo.TOSUBMIT, true, nb);
398             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
399             addRunners(preparedStatement, UpdatedInfo.TOSUBMIT.name(), nb);
400             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
401                     ErrorCode.InitOk, nb);
402             addRunners(preparedStatement, ErrorCode.InitOk.mesg, nb);
403             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
404                     ErrorCode.PreProcessingOk, nb);
405             addRunners(preparedStatement, ErrorCode.PreProcessingOk.mesg, nb);
406             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
407                     ErrorCode.TransferOk, nb);
408             addRunners(preparedStatement, ErrorCode.TransferOk.mesg, nb);
409             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
410                     ErrorCode.PostProcessingOk, nb);
411             addRunners(preparedStatement, ErrorCode.PostProcessingOk.mesg, nb);
412             preparedStatement = null;
413         } catch (GoldenGateDatabaseException e) {
414             if (preparedStatement != null) {
415                 preparedStatement.realClose();
416             }
417             logger.warn("OpenR66 Web Error {}", e.getMessage());
418             sendError(ctx, HttpResponseStatus.SERVICE_UNAVAILABLE);
419             return;
420         }
421         responseContent.append(REQUEST.active.readEnd());
422     }
423 
424     /**
425      * print all transfers in error
426      *
427      * @param ctx
428      * @param nb
429      */
430     private void error(ChannelHandlerContext ctx, int nb) {
431         responseContent.append(REQUEST.error.readHeader(this));
432         DbPreparedStatement preparedStatement = null;
433         try {
434             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
435                     dbSession, UpdatedInfo.INERROR, true, nb / 2);
436             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
437             addRunners(preparedStatement, UpdatedInfo.INERROR.name(), nb / 2);
438             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
439                     dbSession, UpdatedInfo.INTERRUPTED, true, nb / 2);
440             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
441             addRunners(preparedStatement, UpdatedInfo.INTERRUPTED.name(),
442                     nb / 2);
443             preparedStatement = DbTaskRunner.getStepPrepareStatement(dbSession,
444                     TASKSTEP.ERRORTASK, nb / 4);
445             addRunners(preparedStatement, TASKSTEP.ERRORTASK.name(), nb / 4);
446         } catch (GoldenGateDatabaseException e) {
447             if (preparedStatement != null) {
448                 preparedStatement.realClose();
449             }
450             logger.warn("OpenR66 Web Error {}", e.getMessage());
451             sendError(ctx, HttpResponseStatus.SERVICE_UNAVAILABLE);
452             return;
453         }
454         responseContent.append(REQUEST.error.readEnd());
455     }
456 
457     /**
458      * Print all done transfers
459      *
460      * @param ctx
461      * @param nb
462      */
463     private void done(ChannelHandlerContext ctx, int nb) {
464         responseContent.append(REQUEST.done.readHeader(this));
465         DbPreparedStatement preparedStatement = null;
466         try {
467             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
468                     ErrorCode.CompleteOk, nb);
469             addRunners(preparedStatement, ErrorCode.CompleteOk.mesg, nb);
470         } catch (GoldenGateDatabaseException e) {
471             if (preparedStatement != null) {
472                 preparedStatement.realClose();
473             }
474             logger.warn("OpenR66 Web Error {}", e.getMessage());
475             sendError(ctx, HttpResponseStatus.SERVICE_UNAVAILABLE);
476             return;
477         }
478         responseContent.append(REQUEST.done.readEnd());
479     }
480 
481     /**
482      * Print all nb last transfers
483      *
484      * @param ctx
485      * @param nb
486      */
487     private void all(ChannelHandlerContext ctx, int nb) {
488         responseContent.append(REQUEST.all.readHeader(this));
489         DbPreparedStatement preparedStatement = null;
490         try {
491             preparedStatement = DbTaskRunner.getStatusPrepareStatement(dbSession,
492                     null, nb);// means all
493             addRunners(preparedStatement, "ALL RUNNERS: " + nb, nb);
494         } catch (GoldenGateDatabaseException e) {
495             if (preparedStatement != null) {
496                 preparedStatement.realClose();
497             }
498             logger.warn("OpenR66 Web Error {}", e.getMessage());
499             sendError(ctx, HttpResponseStatus.SERVICE_UNAVAILABLE);
500             return;
501         }
502         responseContent.append(REQUEST.all.readEnd());
503     }
504 
505 
506     /**
507      * print only status
508      *
509      * @param ctx
510      * @param nb
511      */
512     private void status(ChannelHandlerContext ctx, int nb) {
513         responseContent.append(REQUEST.status.readHeader(this));
514         DbPreparedStatement preparedStatement = null;
515         try {
516             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
517                     dbSession, UpdatedInfo.INERROR, true, 1);
518             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
519             try {
520                 preparedStatement.executeQuery();
521                 if (preparedStatement.getNext()) {
522                     responseContent.append("<p>Some Transfers are in ERROR</p><br>");
523                     status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
524                 }
525             } finally {
526                 if (preparedStatement != null) {
527                     preparedStatement.realClose();
528                 }
529             }
530             preparedStatement = DbTaskRunner.getSelectFromInfoPrepareStatement(
531                     dbSession, UpdatedInfo.INTERRUPTED, true, 1);
532             DbTaskRunner.finishSelectOrCountPrepareStatement(preparedStatement);
533             try {
534                 preparedStatement.executeQuery();
535                 if (preparedStatement.getNext()) {
536                     responseContent.append("<p>Some Transfers are INTERRUPTED</p><br>");
537                     status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
538                 }
539             } finally {
540                 if (preparedStatement != null) {
541                     preparedStatement.realClose();
542                 }
543             }
544             preparedStatement = DbTaskRunner.getStepPrepareStatement(dbSession,
545                     TASKSTEP.ERRORTASK, 1);
546             try {
547                 preparedStatement.executeQuery();
548                 if (preparedStatement.getNext()) {
549                     responseContent.append("<p>Some Transfers are in ERRORTASK</p><br>");
550                     status = HttpResponseStatus.INTERNAL_SERVER_ERROR;
551                 }
552             } finally {
553                 if (preparedStatement != null) {
554                     preparedStatement.realClose();
555                 }
556             }
557             if (status != HttpResponseStatus.INTERNAL_SERVER_ERROR) {
558                 responseContent.append("<p>No problem is found in Transfers</p><br>");
559             }
560         } catch (GoldenGateDatabaseException e) {
561             if (preparedStatement != null) {
562                 preparedStatement.realClose();
563             }
564             logger.warn("OpenR66 Web Error {}", e.getMessage());
565             sendError(ctx, HttpResponseStatus.SERVICE_UNAVAILABLE);
566             return;
567         }
568         responseContent.append(REQUEST.status.readEnd());
569     }
570     /**
571      * print only status
572      *
573      * @param ctx
574      * @param nb
575      */
576     private void statusxml(ChannelHandlerContext ctx, long nb, boolean detail) {
577         Configuration.configuration.monitoring.run(nb, detail);
578         responseContent.append(Configuration.configuration.monitoring.exportXml(detail));
579     }
580 
581     /**
582      * Write the response
583      *
584      * @param e
585      */
586     private void writeResponse(MessageEvent e) {
587         // Convert the response content to a ChannelBuffer.
588         ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseContent
589                 .toString(), GgStringUtils.UTF8);
590         responseContent.setLength(0);
591         // Decide whether to close the connection or not.
592         boolean keepAlive = HttpHeaders.isKeepAlive(request);
593         boolean close = HttpHeaders.Values.CLOSE.equalsIgnoreCase(request
594                 .getHeader(HttpHeaders.Names.CONNECTION)) ||
595                 (!keepAlive);
596 
597         // Build the response object.
598         HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
599                 status);
600         response.setContent(buf);
601         if (isCurrentRequestXml) {
602             response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/xml");
603         } else {
604             response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html");
605         }
606         if (keepAlive) {
607             response.setHeader(HttpHeaders.Names.CONNECTION,
608                     HttpHeaders.Values.KEEP_ALIVE);
609         }
610         if (!close) {
611             // There's no need to add 'Content-Length' header
612             // if this is the last response.
613             response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String
614                     .valueOf(buf.readableBytes()));
615         }
616 
617         String cookieString = request.getHeader(HttpHeaders.Names.COOKIE);
618         if (cookieString != null) {
619             CookieDecoder cookieDecoder = new CookieDecoder();
620             Set<Cookie> cookies = cookieDecoder.decode(cookieString);
621             if (!cookies.isEmpty()) {
622                 // Reset the cookies if necessary.
623                 CookieEncoder cookieEncoder = new CookieEncoder(true);
624                 for (Cookie cookie: cookies) {
625                     cookieEncoder.addCookie(cookie);
626                     response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder.encode());
627                     cookieEncoder = new CookieEncoder(true);
628                 }
629             }
630         }
631 
632         // Write the response.
633         ChannelFuture future = e.getChannel().write(response);
634 
635         // Close the connection after the write operation is done if necessary.
636         if (close) {
637             future.addListener(ChannelFutureListener.CLOSE);
638         }
639         if (this.isPrivateDbSession && dbSession != null) {
640             dbSession.disconnect();
641             DbAdmin.nbHttpSession--;
642             dbSession = null;
643         }
644     }
645 
646     /**
647      * Send an error and close
648      *
649      * @param ctx
650      * @param status
651      */
652     private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
653         HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
654                 status);
655         response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html");
656         responseContent.setLength(0);
657         responseContent.append(REQUEST.error.readHeader(this));
658         responseContent.append("OpenR66 Web Failure: ");
659         responseContent.append(status.toString());
660         responseContent.append(REQUEST.error.readEnd());
661         response.setContent(ChannelBuffers.copiedBuffer(responseContent
662                 .toString(), GgStringUtils.UTF8));
663         // Close the connection as soon as the error message is sent.
664         ctx.getChannel().write(response).addListener(
665                 ChannelFutureListener.CLOSE);
666     }
667 
668     @Override
669     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
670             throws Exception {
671         OpenR66Exception exception = OpenR66ExceptionTrappedFactory
672                 .getExceptionFromTrappedException(e.getChannel(), e);
673         if (exception != null) {
674             if (!(exception instanceof OpenR66ProtocolBusinessNoWriteBackException)) {
675                 if (e.getCause() instanceof IOException) {
676                     if (this.isPrivateDbSession && dbSession != null) {
677                         dbSession.disconnect();
678                         DbAdmin.nbHttpSession--;
679                         dbSession = null;
680                     }
681                     // Nothing to do
682                     return;
683                 }
684                 logger.warn("Exception in HttpHandler {}", exception.getMessage());
685             }
686             if (e.getChannel().isConnected()) {
687                 sendError(ctx, HttpResponseStatus.BAD_REQUEST);
688             }
689         } else {
690             if (this.isPrivateDbSession && dbSession != null) {
691                 dbSession.disconnect();
692                 dbSession = null;
693             }
694             // Nothing to do
695             return;
696         }
697     }
698 
699     /*
700      * (non-Javadoc)
701      *
702      * @see
703      * org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelClosed(org
704      * .jboss.netty.channel.ChannelHandlerContext,
705      * org.jboss.netty.channel.ChannelStateEvent)
706      */
707     @Override
708     public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
709             throws Exception {
710         logger.debug("Closed");
711         super.channelClosed(ctx, e);
712         if (this.isPrivateDbSession && dbSession != null) {
713             dbSession.disconnect();
714             DbAdmin.nbHttpSession--;
715         }
716     }
717 
718     /*
719      * (non-Javadoc)
720      *
721      * @see
722      * org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected
723      * (org.jboss.netty.channel.ChannelHandlerContext,
724      * org.jboss.netty.channel.ChannelStateEvent)
725      */
726     @Override
727     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
728             throws Exception {
729         logger.debug("Connected");
730         authentHttp.getAuth().specialNoSessionAuth(false, Configuration.configuration.HOST_ID);
731         super.channelConnected(ctx, e);
732         ChannelGroup group = Configuration.configuration.getHttpChannelGroup();
733         if (group != null) {
734             group.add(e.getChannel());
735         }
736     }
737 }