001    /** =====================================================================       
002    *
003    *  File Name : $Id: FaultServiceImpl.java,v 1.47 2008/01/15 11:08:14 cb Exp $
004    *
005    *  Description
006    *  -----------
007    *
008    *  See javadoc comment 
009    * 
010    *  =====================================================================
011    *
012    *   @Author : Craige Bevil 
013    *             Control Software Group
014    *             Isaac Newton Group of Telescopes
015    *
016    *  =====================================================================
017    *
018    *     Modification Log
019    *
020    *     Vers         Date        Author       Reason
021    *     ----         ----        ------       ------
022    *      1            9 May 2007 C.Bevil      First Release
023    *
024    *     Commissioning Notes
025    *     -------------------
026    *
027    *     None
028    *     
029    *  =====================================================================
030    *
031    *     @Version   : $Id: FaultServiceImpl.java,v 1.47 2008/01/15 11:08:14 cb Exp $
032    *
033    *     @Author    : $Author: cb $
034    *
035    *     Header     : $Header: /opt/INGsrc/src/CVS/softproj/FaultDatabase/src/FaultDatabase/FaultDatabase/src/GWTApplication/server/FaultServiceImpl.java,v 1.47 2008/01/15 11:08:14 cb Exp $
036    *
037    *     Log        : $Log: FaultServiceImpl.java,v $
038    *     Log        : Revision 1.47  2008/01/15 11:08:14  cb
039    *     Log        : Ran through PMD and sorted out the javadoc so that we could export the
040    *     Log        : javadoc to the javadoc repository.
041    *     Log        :
042    *     Log        : Revision 1.46  2008/01/14 17:18:15  cb
043    *     Log        : Updated to include a mailshot which will mail the details of the
044    *     Log        : faults which are in the pending state to opshead and the person which
045    *     Log        : is assigned the fault.
046    *     Log        :
047    *     Log        : Revision 1.45  2007/12/17 12:30:47  cb
048    *     Log        : Typo fixed
049    *     Log        :
050    *     Log        : Revision 1.44  2007/12/17 11:30:45  cb
051    *     Log        : Revised the list of fault listeners which are returned to the client
052    *     Log        : so only those faults which are not closed are returned just to keep
053    *     Log        : the list down.
054    *     Log        :
055    *     Log        : Modified the formatting of the HTML when preparing it to be printed
056    *     Log        : into the PDF file to ensure that the <br .....> tag is maintained
057    *     Log        : correctly.
058    *     Log        :
059    *     Log        : Revision 1.43  2007/12/17 10:36:58  cb
060    *     Log        : Corrected a problem with printing out the faults from the previous 24
061    *     Log        : hours (see DR 17947)
062    *     Log        :
063    *     Log        : Revision 1.42  2007/12/14 12:33:55  cb
064    *     Log        : Removed dead code
065    *     Log        :
066    *     Log        : Revision 1.41  2007/12/14 07:52:27  cb
067    *     Log        : Update the task which sends out mailshots so that it sends out the
068    *     Log        : details of the faults which are in the new state for more than 48
069    *     Log        : hours.
070    *     Log        :
071    *     Log        : Revision 1.40  2007/12/12 15:29:21  cb
072    *     Log        : Included the code which will print out the faults on the ops
073    *     Log        : printer. Corrected some SQL and added some debug
074    *     Log        :
075    *     Log        : Revision 1.39  2007/12/12 12:03:14  cb
076    *     Log        : Fixed a bug in the reporting of new faults to teh user
077    *     Log        :
078    *     Log        : Revision 1.38  2007/10/18 13:18:34  cb
079    *     Log        : Updated the name of the user associated with the emails which are sent
080    *     Log        : by the system.
081    *     Log        :
082    *     Log        : Revision 1.37  2007/10/18 08:43:49  cb
083    *     Log        : Support for decommissioned instruments has been added to the system
084    *     Log        :
085    *     Log        : Revision 1.36  2007/10/18 07:58:02  cb
086    *     Log        : Changed the person from whom the emails are sent from to
087    *     Log        : faultdbadmin.
088    *     Log        :
089    *     Log        : Revision 1.35  2007/10/08 13:08:29  cb
090    *     Log        : Modified the packFault method so that it can pack fault data from
091    *     Log        : either a fault view or a fault table.
092    *     Log        :
093    *     Log        : Revision 1.34  2007/10/03 00:00:34  cb
094    *     Log        : Modified the margin at the top of the PDF documents. Now prints off the
095    *     Log        : documents at the ops_laser.
096    *     Log        :
097    *     Log        : Revision 1.33  2007/10/02 13:08:09  cb
098    *     Log        : Send the mail shot at 7 AM only
099    *     Log        :
100    *     Log        : Revision 1.32  2007/09/26 15:20:14  cb
101    *     Log        : Corrected some spellings of the word occurred
102    *     Log        :
103    *     Log        : Revision 1.31  2007/09/26 09:42:52  cb
104    *     Log        : Added a new timer task which will inform management when a fault has
105    *     Log        : been in the NEW state for more than 48 hours.
106    *     Log        :
107    *     Log        : Revision 1.30  2007/09/26 08:14:55  cb
108    *     Log        : New faults are printed to wht_laser at 0830 every morning
109    *     Log        :
110    *     Log        : Revision 1.29  2007/09/25 11:05:13  cb
111    *     Log        : Corrected a problem when printing as we were printing on the default
112    *     Log        : printer only and essentially ignoring the printer which was specified
113    *     Log        : by the user.
114    *     Log        :
115    *     Log        : Revision 1.28  2007/09/06 14:07:02  cb
116    *     Log        : Reduced the number of items returned in a search to 250
117    *     Log        :
118    *     Log        : Revision 1.27  2007/09/06 12:57:32  cb
119    *     Log        : Updated a comment
120    *     Log        :
121    *     Log        : Revision 1.26  2007/09/06 12:57:07  cb
122    *     Log        : Added some extra debug
123    *     Log        :
124    *     Log        : Revision 1.25  2007/09/06 12:54:51  cb
125    *     Log        : Reverted the method of checking for fault updates.
126    *     Log        :
127    *     Log        : Use log4j for logging
128    *     Log        :
129    *     Log        : Revision 1.23  2007/09/03 13:21:17  cb
130    *     Log        : Modified so that if the start and the end dates which have been
131    *     Log        : specified in the advanced and the basic search are entered with the
132    *     Log        : start date greater than the end date, the software swaps them around.
133    *     Log        :
134    *     Log        : Revision 1.22  2007/09/03 11:21:43  cb
135    *     Log        : Sorted out the problem of the fault not being printed correctly on an
136    *     Log        : A4 piece of paper
137    *     Log        :
138    *     Log        : Revision 1.21  2007/08/22 11:15:41  cb
139    *     Log        : Update the agorithm which strips HTML from text before inserting it
140    *     Log        : into PDF files.
141    *     Log        :
142    *     Log        : Revision 1.20  2007/08/21 13:39:02  cb
143    *     Log        : Rewrote the advanced search algorithm taking into account the
144    *     Log        : conjugated text field which was introduced.
145    *     Log        :
146    *     Log        : Revision 1.19  2007/08/21 09:21:01  cb
147    *     Log        : Modified so that in the advanced search we can specifiy either of the
148    *     Log        : start or end date
149    *     Log        :
150    *     Log        : Revision 1.18  2007/08/20 14:03:49  cb
151    *     Log        : Update the assigneddate if the fault has been assigned to somebody.
152    *     Log        :
153    *     Log        : Revision 1.17  2007/08/20 11:02:11  cb
154    *     Log        : Added an option which allow the usr to specify an boolean OR on the
155    *     Log        : search terms in the keywords
156    *     Log        :
157    *     Log        : Revision 1.16  2007/08/17 14:28:17  cb
158    *     Log        : Do not return the state RELEASED as part of the state list. Fixed a
159    *     Log        : bug when updating the workarounds. Revised the manner in which the
160    *     Log        : application informs listeners.
161    *     Log        :
162    *     Log        : Revision 1.15  2007/08/16 14:55:06  cb
163    *     Log        : Added a link to the fault details which are mailed to the users.
164    *     Log        :
165    *     Log        : Revision 1.14  2007/08/16 14:35:15  cb
166    *     Log        : Modified to use the new timelost_interval field in teh fault view in
167    *     Log        : order to perform searches on the time lost attribute.
168    *     Log        :
169    *     Log        : Revision 1.13  2007/08/16 13:31:44  cb
170    *     Log        : Updated the search of the simple search to allow for the terms
171    *     Log        : which are added to the description and the title fields.
172    *     Log        :
173    *     Log        : Updated the advanced search to allow for textual entry of the priority
174    *     Log        : field so that the user can specify <>
175    *     Log        :
176    *     Log        : Revision 1.12  2007/08/15 11:52:55  cb
177    *     Log        : Fixed up the advanced search
178    *     Log        :
179    *     Log        : Revision 1.11  2007/08/15 09:51:52  cb
180    *     Log        : Fixed a serious bug in inserting the solution fields.
181    *     Log        :
182    *     Log        : Revision 1.10  2007/08/14 09:54:15  cb
183    *     Log        : Before modifying the method through which the faults are updated
184    *     Log        :
185    *     Log        : Revision 1.9  2007/08/13 13:57:45  cb
186    *     Log        : Modified the SQL which looks up fault links to optimize it was too
187    *     Log        : slow.
188    *     Log        :
189    *     Log        : Revision 1.8  2007/08/02 12:22:54  cb
190    *     Log        : Modified to support the extra tabs in the quick view tab panel to
191    *     Log        : include the different groups of outstanding faults.
192    *     Log        :
193    *     Log        : Revision 1.7  2007/08/01 13:00:02  cb
194    *     Log        : First prototype after import
195    *     Log        :
196    *     Log        : Revision 1.6  2007/07/26 13:35:24  cb
197    *     Log        : Updated the HTML report generation
198    *     Log        :
199    *     Log        : Revision 1.5  2007/07/19 11:37:07  cb
200    *     Log        : Added the lastmodified field to the fault table and views which is
201    *     Log        : updated when anything is changed in the fault.
202    *     Log        :
203    *     Log        : Added a new method which can be used to close down the fault.
204    *     Log        :
205    *     Log        : Revision 1.4  2007/07/13 10:54:00  cb
206    *     Log        : Complete interface prototype
207    *     Log        :
208    *     Log        : Revision 1.3  2007/07/03 10:05:46  cb
209    *     Log        : The email feature has now been completed.
210    *     Log        :
211    *     Log        : Revision 1.2  2007/07/02 16:21:15  cb
212    *     Log        : Before changing the email to send HTML messages
213    *     Log        :
214    *     Log        : Revision 1.1.1.1  2007/06/01 08:33:25  cb
215    *     Log        : Imported using TkCVS
216    *     Log        :
217    *
218    * =====================================================================*/
219    
220    package GWTApplication.server;
221    
222    import GWTApplication.client.*;
223    import com.google.gwt.user.server.rpc.*;
224    import java.util.*;
225    import java.text.*;
226    
227    import javax.sql.DataSource;
228    import java.sql.*;
229    import java.io.*;
230    
231    // SNMP packages 
232    
233    import org.snmp4j.*;
234    import org.snmp4j.security.*;
235    import org.snmp4j.transport.*;
236    import org.snmp4j.mp.*;
237    import org.snmp4j.smi.*;
238    import org.snmp4j.event.*;
239    
240    // Used for emailing clients about updates to faults.
241    
242    import javax.mail.*;
243    import javax.mail.internet.*;
244    
245    // Used for printing 
246    
247    import javax.print.*;
248    import javax.print.attribute.*;
249    import javax.print.attribute.standard.*;
250    import javax.print.event.*;
251    
252    // Used for creating PDF documents (this stuff is so so cool)
253    
254    import com.lowagie.text.*;
255    import com.lowagie.text.pdf.*;
256    import com.lowagie.text.html.*;
257    
258    // log4j 
259    
260    import org.apache.log4j.Logger;
261    import org.apache.log4j.Level;
262    import org.apache.log4j.PropertyConfigurator;
263    
264    import java.util.Date;
265    
266    /**
267     * This class implments all of the services which will be used by the
268     * fault database application to support the client AJAX/GWT
269     * componentry which runs on the client browser. This class is
270     * responsible for performing the business logic associated with the fault
271     * management system. 
272     * <P>
273     * This class makes use of the SNMP4J classes which are use to allow
274     * the class to send SNMP TRAPS to the SNMP manager to inform it that
275     * new faults have been raised. 
276     * <P> 
277     * The class uses a database pool which is set up by the tomcat
278     * container which will permit the class rapid access to the
279     * database. The configuration of the database pool is part of the
280     * tomcat configuration and <b>not</b> the configuraiton of the fault
281     * management system.
282     * <P>
283     * Aside from SNMP4J API JavaDoc, one should look at the source code
284     * for the class SnmpRequest which is a console based class which can
285     * be used to create a SNMP manager/agent and is the best place to
286     * look if you want to change the SNMP code in this class as there is
287     * precious little documentation elsewhere.
288     *<P>
289     * @author Craige Bevil 
290     * @version $Id: FaultServiceImpl.java,v 1.47 2008/01/15 11:08:14 cb Exp $
291     */
292    
293    public class FaultServiceImpl extends RemoteServiceServlet implements FaultService {
294    
295        enum AdvancedDataType {STRING,NONSTRING};
296        
297        /**
298         * This is a timer task which will be used to print the new faults
299         * everyday in the ops room in the morning
300         */
301        
302        private final PrintNewFaultsInLastTwentyFourHours printNewFaultsInLastTwentyFourHrsTimerTask = new PrintNewFaultsInLastTwentyFourHours();
303    
304        /**
305         * A timer task which is used to inform the relevant people when a
306         * fault has been left in the new state for more than 48 hours
307         */
308        
309        private final MailManagementUnattendedFaults MailManagementUntendedFaultsTimerTask = new MailManagementUnattendedFaults();
310    
311        /**
312         * This is a timer task which will report faults which have been
313         * state pending for more than seven days.
314         */
315    
316        private final NotifyPendingFaults NotifyPendingFaultsTimerTask = new NotifyPendingFaults();
317        
318        /**
319         * This is a timer object which will be used for scheduling the
320         * printing of the new faults to the ops room printer
321         */
322        
323        private final Timer PrintNewFaultsTimer = new Timer ();
324    
325        /**
326         * This is a timer object which will be used for scheduling the
327         * report of unattended faults in the system to management.
328         */
329        
330        private final Timer ProduceUnattendedFaultsMailShotTimer = new Timer ();
331    
332        /**
333         * This is a timer object which will be used for scheduling the
334         * report of pending faults in the system to the assigned.
335         */
336        
337        private final Timer ProduceOutstandingPendingFaultsMailShotTimer = new Timer ();
338        
339        /**
340         * Maximum number of items returned by database in one search 
341         */
342        
343        private final static int MAXNUMBEROFRESULTS = 250;
344        
345        /**
346         * Used for logging 
347         */
348        
349        private Logger logger;
350                
351        /**
352         * An enumeration of the list of fields in the fault table which
353         * contain text which is suitable to be searched as such. DO NOT
354         * CHANGE the order of these enums, especially the last
355         * two. The TITLE and the DESCRIPTION enums should <b> always</b>
356         * remain at the end. 
357         */
358        
359        enum TextFieldsToSearch {
360                OBSERVER,
361                TO_NAME,
362                TO_SURNAME,
363                TO_EMAIL,
364                SA_NAME,
365                SA_SURNAME,
366                SA_EMAIL,
367                FAULTTYPE,
368                ASSIGNEDTO_NAME,
369                ASSIGNEDTO_SURNAME,
370                ASSIGNEDTO_EMAIL,
371                ENTEREDBY_NAME,
372                ENTEREDBY_SURNAME,
373                ENTEREDBY_EMAIL,
374                STATE,
375                SYSTEM,
376                SITE,
377                SEVERITY,
378                INSTRUMENT,
379                DUTYTECHNICIAN_NAME,
380                DUTYTECHNICIAN_SURNAME,
381                DUTYTECHNICIAN_EMAIL,
382                CONJUGATEDTEXTFIELD
383                    };
384    
385        /**
386         * This is the host which the SMNP TRAP fault alerts will be sent
387         * to
388         */
389        
390        org.snmp4j.smi.Address targetAddress;
391    
392        /**
393         * This is the transport protocol which we will use which in our
394         * case will be UDP.
395         */
396        
397        TransportMapping transport;
398    
399        /**
400         * This is the SNMP class which will be used to communicate with
401         * the SNMP manager 
402         */
403        
404        Snmp snmp;
405        
406        /**
407         * The user based security model which will be used to when
408         * sending messages to the SNMP server
409         */
410        
411        USM usm;
412    
413        /**
414         * Used to modify the global properties of the application
415         */
416        
417        private Properties props;
418        
419        /**
420         * This is a data resource from the servlet container which
421         * contains a postgres data source
422         */
423            
424        private DataSource dataSource;
425        
426        private static final long sealVersionUID= -1502084255979334403L;
427    
428        /**
429         * Used to set the data source for this module 
430         * @param dataSource The new data source
431         */
432        
433        public void set_dataSource (DataSource dataSource) {
434            this.dataSource = dataSource;
435        }
436    
437        /**
438         * This is a timer task which will mail the person assigned a
439         * fault if the fault is in a <b>pending</b> state for more than 7
440         * days. This task is run every day at 0700.
441         */
442        
443        class NotifyPendingFaults extends TimerTask {
444            
445            public void run () {
446            
447                Connection connection  = null;
448                Statement  stmt        = null;
449                ResultSet  rs          = null;
450    
451                final ArrayList<Fault> pendingFaults = new ArrayList<Fault>();
452                
453                logger.info("Checking to see if there are pending faults over one week old in the database to be reported.");
454                                
455                if (dataSource == null) {
456                    dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
457                }
458                
459                try {
460    
461                    connection = dataSource.getConnection();
462                    stmt       = connection.createStatement();
463    
464                    // Find all pending faults which are greater than 7
465                    // days old.  Nice use of intervals here I say,
466                    // databases are lovely.
467    
468                    rs = stmt.executeQuery("select * from faultview_en where timeentered <= cast(cast('now' as date) - cast ('7 day' as interval) as date) and state = 'Pending' order by id desc");
469                
470                    while (rs.next()) {
471                        Fault faultData = packFault(rs,true);
472                        pendingFaults.add(faultData);
473                    } 
474    
475                    // Ensure that we clean up the database connection
476                    // correctly so that the connection is returned to the
477                    // database connection pool
478    
479                    rs.close();
480                    connection.close();
481                    stmt.close();
482                
483                    rs             = null;
484                    connection = null;
485                    stmt       = null;
486    
487                } catch (SQLException e) {
488                    e.printStackTrace();
489                } finally {
490               
491                    // Always make sure result sets and statements are closed,
492                    // and the connection is returned to the pool
493    
494                    closeDatabaseConnections(connection,stmt,rs);               
495                }
496    
497                // Now we need to go through all of the new faults which
498                // have been found and then create an HTML report which we
499                // will send to the list of recipients
500    
501                StringBuffer pendingFaultReport;
502                
503                for (Fault currentFault : pendingFaults) { 
504                    
505                    pendingFaultReport = new StringBuffer();
506    
507                    try {
508    
509                        pendingFaultReport.append(fetchHTMLFaultReport(currentFault.id,true));
510                        pendingFaultReport.append("<P>");
511    
512                    } catch (UnknownFaultSpecifiedException e) {
513                        logger.error("Unable to find fault " + currentFault.id + " " + e.getMessage());
514                        return;
515                    }               
516                    
517                    // Now for each of the managers and the person who is
518                    // assigned the fault, they should be sent an email
519                    // which notifies them that this fault has been in a
520                    // pending state for more than 7 days and that they
521                    // should do something about it.
522                    
523                    try {
524                        
525                        final javax.mail.Session session = javax.mail.Session.getInstance(props, null);
526                        
527                        final Message msg = new MimeMessage(session);
528                        
529                        msg.setFrom(new InternetAddress("faultdbadmin@ing.iac.es","Fault Database Admin"));
530                                        
531                        msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("opshead@ing.iac.es", false));                                        
532                        
533                        // If there is a person which is assigned the
534                        // fault then he should be sent the email as well.
535    
536                        if (!currentFault.assignedToEmail.equals("UNKNOWN")) {
537                            msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(currentFault.assignedToEmail, false));                                            
538                        } else {
539                            logger.error("There is no person which has been assigned fault " + currentFault.id + " to inform ");
540                        }
541    
542                        // Now set the content of the email to show the
543                        // details of the fault
544    
545                        msg.setSubject("Pending faults in fault database / Defectos pendientes en el base de datos de defectos");
546                        
547                        msg.setContent("You have been sent this email by the <b>Fault Management System</B> to inform you that the following fault has been in the state <b>pending</b> for more than seven days and it's status should now be updated.<P>" + 
548                                       "<P> El sistema de defectos te ha mandando este correo para informarte que el defecto siguiente ha sido en el estado pendiente durante mas de siete dias. Debería actualizar el estado de este defecto. <P><P> " + 
549                                       pendingFaultReport.toString(), "text/html");
550                        
551                        msg.setHeader("X-Mailer", "Fault Management System");
552                        
553                        msg.setSentDate(new java.util.Date());
554                        
555                        logger.info("Sending email for fault " + currentFault.id + " as it has been in state pending for more than 7 days");
556    
557                        // Now send the message to the email to the
558                        // current recipient
559                        
560                        Transport.send(msg);
561                        
562                    } catch (Exception e) {
563                        logger.error("Unable to send pending faults report " + e.getMessage());
564                    }
565                }
566                
567                logger.info("Pending faults mailshot has been sent");
568            }       
569        }
570    
571        /**
572         * This is a timer task which will be used to mail a list of
573         * recipients the details of all faults which have remained in the
574         * <b>new</b> state for more than 48 hours.
575         */
576        
577        class MailManagementUnattendedFaults extends TimerTask {
578            
579            /**
580             * Called when the timertask is scheduled to run. It finds all
581             * of the faults which have been in the new state for more
582             * than 48 hours and then mails the details of them to
583             * management. This thread is run twice a day at 0700 and
584             * 1900.
585             * <P>
586             * Currently the people who are informed are Juerg and Michiel. 
587             */
588            
589            public void run () {
590                
591                // A list of people who are to be informed of unattended
592                // faults.
593    
594                final String[] MANAGEMENTMAILRECIPIENTS = {
595                    "opshead@ing.iac.es",
596                    "cb@ing.iac.es"
597                };
598                
599                Connection connection  = null;
600                Statement  stmt        = null;
601                ResultSet  rs          = null;
602    
603                final ArrayList<Fault> newFaults = new ArrayList<Fault>();
604                
605                logger.info("Checking to see if there are unattended faults to be reported");
606                                
607                if (dataSource == null) {
608                    dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
609                }
610                
611                try {
612    
613                    connection = dataSource.getConnection();
614                    stmt       = connection.createStatement();
615    
616                    // Find all faults which have been entered in the past
617                    // 24 hours, nice use of intervals here I say. 
618    
619                    rs = stmt.executeQuery("select * from faultview_en where timeentered <= cast(cast('now' as date) - cast ('2 day' as interval) as date) and state = 'New' order by id desc");
620                
621                    while (rs.next()) {
622                        Fault faultData = packFault(rs,true);
623                        newFaults.add(faultData);
624                    } 
625    
626                    // Ensure that we clean up the database connection
627                    // correctly so that the connection is returned to the
628                    // database connection pool
629    
630                    rs.close();
631                    connection.close();
632                    stmt.close();
633                
634                    rs             = null;
635                    connection = null;
636                    stmt       = null;
637    
638                } catch (SQLException e) {
639                    e.printStackTrace();
640                } finally {
641               
642                    // Always make sure result sets and statements are closed,
643                    // and the connection is returned to the pool
644    
645                    closeDatabaseConnections(connection,stmt,rs);               
646                }
647    
648                // Now we need to go through all of the new faults which
649                // have been found and then create an HTML report which we
650                // will send to the list of recipients
651    
652                StringBuffer newFaultsUnattended = new StringBuffer();
653                
654                boolean faultsFound = false;
655    
656                for (Fault currentFault : newFaults) { 
657                    
658                    faultsFound = true;
659                    
660                    try {
661    
662                        newFaultsUnattended.append(fetchHTMLFaultReport(currentFault.id,true));
663                        newFaultsUnattended.append("<P>");
664    
665                    } catch (UnknownFaultSpecifiedException e) {
666                        logger.error("Unable to find fault " + currentFault.id + " " + e.getMessage());
667                        return;
668                    }
669                }
670    
671                // If no faults where found then we have nothing to do here. 
672    
673                if (!faultsFound) {
674                    logger.info("No unattended faults were found, no report sent to opshead");
675                    return;
676                }
677                
678                // Now for each of the managers which are to be informed,
679                // let them know of the unattended faults.
680    
681                for (String emailAddress : MANAGEMENTMAILRECIPIENTS) {
682    
683                    try {
684                        
685                        final javax.mail.Session session = javax.mail.Session.getInstance(props, null);
686                    
687                        final Message msg = new MimeMessage(session);
688                        
689                        msg.setFrom(new InternetAddress("faultdbadmin@ing.iac.es","Fault Database Admin"));
690                        
691                        msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(emailAddress, false));
692                        
693                        msg.setSubject("Unattended faults in the Fault Management System have been found");
694                        
695                        msg.setContent("You have been sent this email by the <b>Fault Management System</B> to inform you of the following faults which have been found in the database which have been in the state <b>NEW</B> for more than 48 hours now. <P><P> " + newFaultsUnattended.toString(), "text/html");
696                        
697                        msg.setHeader("X-Mailer", "Fault Management System");
698                        
699                        msg.setSentDate(new java.util.Date());
700                        
701                        // Now send the message to the email to the current 
702                        // recipient
703                        
704                        Transport.send(msg);
705                        
706                    } catch (Exception e) {
707                        logger.error("Unable to send unattended faults report to " + emailAddress + " " + e.getMessage());
708                    }
709                }
710                
711                logger.info("Unattended faults mailshot has been sent");
712            }       
713        }
714        
715        /**
716         * This is a timertask which will be used to print all <b>new</B>
717         * faults in the previous 24 hours to the printer in the
718         * operations room.
719         */
720         
721        class PrintNewFaultsInLastTwentyFourHours extends TimerTask {
722            
723            /**
724             * Called when the timertask is scheduled to run. It finds all
725             * of the faults which have occurred in the last 24 hours and
726             * then prints them to the printer in the operations
727             * room. This should take place at 0830 every morning.
728             */
729            
730            public void run () {
731                
732                // This is the default destination for faults which are to
733                // be printed
734    
735                final String OPSPRINTER = "ops_laser";
736                
737                Connection connection  = null;
738                Statement  stmt = null;
739                ResultSet  rs   = null;
740                
741                final ArrayList<Fault> newFaults = new ArrayList<Fault>();
742            
743                logger.info("Printing faults from the last 24 hours to ops printer");
744                
745                if (dataSource == null) {
746                    dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
747                }
748                
749                try {
750    
751                    connection = dataSource.getConnection();
752                    stmt       = connection.createStatement();
753    
754                    // Find all faults which have been entered in the past
755                    // 24 hours, nice use of intervals here I say.  See
756                    // http://www.postgresql.org/docs/techdocs.13 for more
757                    // information about intervals, timestamps etc. 
758    
759                    // The following line has been changed after DR17947
760    
761                    rs = stmt.executeQuery("select * from faultview_en where timeentered >= cast('now' as timestamp) - cast ('1 day' as interval) order by id desc");        
762    
763                    while (rs.next()) {
764    
765                        Fault faultData = packFault(rs,true);
766                        newFaults.add(faultData);
767                    } 
768    
769                    // Ensure that we clean up the database connection
770                    // correctly so that the connection is returned to the
771                    // database connection pool
772    
773                    rs.close();
774                    connection.close();
775                    stmt.close();
776                
777                    rs             = null;
778                    connection = null;
779                    stmt       = null;
780    
781                } catch (SQLException e) {
782                    e.printStackTrace();
783                } finally {
784               
785                    // Always make sure result sets and statements are closed,
786                    // and the connection is returned to the pool
787    
788                    closeDatabaseConnections(connection,stmt,rs);               
789                }
790    
791                // Now we need to go through all of the faults which have
792                // been found and then print them out
793                
794                for (Fault currentFault : newFaults) { 
795                    
796                    try {
797                        printFault(currentFault.id,OPSPRINTER,true);
798                    } catch (UnknownPrinterSpecifiedException e) {
799                        logger.error("Unable to print to printer " + OPSPRINTER + " : " + e.getMessage());
800                        return;
801                    }
802                }
803                
804                logger.info("New faults printed for operations on " + OPSPRINTER);
805            }
806        }
807    
808        /**
809         * Constructor. This initialises the log4j and SMTP handling.  It
810         * also starts timer threads which will be used to print the
811         * details of the new faults to the printer in the ops room and
812         * also remind management when there is a fault which has been in
813         * the <b>NEW</b> state for more than 48 hours.
814         */
815         
816        public void init () {
817            
818            // Initialise log4j
819    
820            String config = getServletContext().getRealPath("/") + getInitParameter("setup");
821            PropertyConfigurator.configure(config);
822    
823            logger = Logger.getLogger("GWTApplication.server.FaultServiceImpl");
824    
825            logger.info("Initialising the Fault Management Servlet Vers $Id: FaultServiceImpl.java,v 1.47 2008/01/15 11:08:14 cb Exp $");
826            
827            // Now sort out the java mail initialisation so that we send
828            // email
829    
830            props = System.getProperties();
831            props.put("mail.smtp.host","smtphost.ing.iac.es");      
832    
833            // Now we need to initialise the SNMP handling so that we can
834            // send alerts when a fault is raised.
835    
836            try { 
837    
838                targetAddress = GenericAddress.parse("udp:snmppc.ing.iac.es/162");
839                transport     = new DefaultUdpTransportMapping();
840                snmp          = new Snmp(transport);
841                usm           = new USM(SecurityProtocols.getInstance(),new OctetString(MPv3.createLocalEngineID()), 0);
842    
843                SecurityModels.getInstance().addSecurityModel(usm);
844                transport.listen();
845    
846            } catch (IOException e) {
847                logger.error("Unable to initialise snmp " + e);
848            }       
849            
850            // Now start a timer task which will be used to print out the
851            // details of all of the new faults in the previous 24 hours
852            // to the ops room printer at 0830 every morning
853            
854            GregorianCalendar calendar = new GregorianCalendar();
855    
856            // Set the time of the calender to 0830
857    
858            calendar.set(Calendar.HOUR_OF_DAY,8);           
859            calendar.set(Calendar.MINUTE,30);
860    
861            // Add one day to get the time at 0830 tomorrow morning 
862            
863            calendar.add(Calendar.DAY_OF_YEAR,1);
864            
865            final Date eightThirtyTomorrowMorning = calendar.getTime();
866    
867            // Now schedule printing of all new faults at 0830 the next
868            // morning and then subsequently all following mornings at the
869            // same time
870            
871            PrintNewFaultsTimer.scheduleAtFixedRate(printNewFaultsInLastTwentyFourHrsTimerTask,eightThirtyTomorrowMorning,24 * 60 * 60 * 1000);     
872    
873            // Schedule the generation of reports of faults which have
874            // been left unattended for more than 48 hours to be sent to
875            // various users
876    
877            calendar = new GregorianCalendar();
878    
879            // Set the time of the calender to 0700
880    
881            calendar.set(Calendar.HOUR_OF_DAY,7);
882            calendar.set(Calendar.MINUTE,0);
883            
884            // Add one day to get the time in millisecs at 0700 tomorrow
885            // morning.
886    
887            calendar.add(Calendar.DAY_OF_YEAR,1);
888    
889            final Date sevenAMTomorrowMorning = calendar.getTime();
890            
891            ProduceUnattendedFaultsMailShotTimer.scheduleAtFixedRate(MailManagementUntendedFaultsTimerTask,sevenAMTomorrowMorning,24 * 60 * 60 * 1000);
892            
893            // Now create the timer task which send emails to the person
894            // assigned to a fault which has been in the pending state for
895            // more than 7 days. We do this every seven days. 
896            
897            ProduceOutstandingPendingFaultsMailShotTimer.scheduleAtFixedRate(NotifyPendingFaultsTimerTask,sevenAMTomorrowMorning, 7 * 24 * 60 * 60 * 1000);
898        }
899        
900        /**
901         * Get the list of people as specified by the caller. It takes as
902         * an input parameter the name of database table which contains
903         * the data. 
904         * <P>
905         * @param DatabaseView The name of the database view which
906         * contains the details of the group of people to return. 
907         * @param Exclude the people which are in this table. If this
908         * value is null then we do not exclude anything.
909         * @param IncludeStaffWhichHaveLeft Include staff which have left the organisation. 
910         * @throws UnableToGetListException If there is a problem getting
911         * the list back from the database     
912         * @return ArrayList<Person> An ArrayList of People. 
913         * @gwt.typeArgs <GWTApplication.client.Person> 
914         */
915    
916        private ArrayList<Person> getPersonDetails (final String DatabaseView,
917                                                    final String ExclusionTable,
918                                                    final boolean IncludeStaffWhichHaveLeft) throws UnableToGetListException {
919    
920            final ArrayList<Person> People = new ArrayList<Person>();
921    
922            Connection connection  = null;
923            Statement  stmt = null;
924            ResultSet  rs   = null;
925    
926            if (dataSource == null) {
927                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
928            }
929                    
930            // Now get a database connection from the connection pool if
931            // we can
932            
933            try {
934                
935                Person newPerson;
936                
937                connection = dataSource.getConnection();
938                stmt       = connection.createStatement();
939                        
940                if (ExclusionTable == null) {
941    
942                    // Do we include the members of staff which are not with us any longer? 
943                    
944                    if (IncludeStaffWhichHaveLeft) {
945                        rs = stmt.executeQuery("select * from " + DatabaseView + " order by surname");                                  
946                    } else {
947                        rs = stmt.executeQuery("select * from " + DatabaseView + " where stillwithing = 'TRUE' order by surname");
948                    }
949                    
950                } else {
951    
952                    // Do we include the members of staff which are not with us any longer? 
953    
954                    if (IncludeStaffWhichHaveLeft) {
955                                        
956                        rs = stmt.executeQuery("select * from " + DatabaseView + " where " + DatabaseView + ".email not in (select email from " + ExclusionTable + ") and staffmember.username not in ('observer','unknown') order by surname");
957                    } else {
958                        rs = stmt.executeQuery("select * from " + DatabaseView + " where stillwithing = 'true' and " + DatabaseView + ".email not in (select email from " + ExclusionTable + ") and staffmember.username not in ('observer','unknown') order by surname");
959                    }
960    
961                    logger.debug("select * from " + DatabaseView + " where " + DatabaseView + ".email not in (select email from " + ExclusionTable + ") and staffmember.username not in ('observer','unknown') order by surname") ;
962                }
963    
964                while (rs.next()) {
965    
966                    newPerson = new Person();
967    
968                    newPerson.setSurname(rs.getString("surname"));
969                    newPerson.setName(rs.getString("name"));
970                    newPerson.setEmailAddress(rs.getString("email"));
971    
972                    People.add(newPerson);
973                }
974    
975                // Ensure that we clean up the database connection
976                // correctly so that the connection is returned to the
977                // database connection pool
978    
979                rs.close();
980                connection.close();
981                stmt.close();
982                
983                rs         = null;
984                connection = null;
985                stmt       = null;
986                
987            } catch (SQLException e) {
988                e.printStackTrace();
989                throw new UnableToGetListException("Unable to get list of people : " + e.getMessage());
990            } finally {
991    
992                // Always make sure result sets and statements are closed,
993                // and the connection is returned to the pool
994    
995                closeDatabaseConnections(connection,stmt,rs);                   
996            }
997    
998            // Now return the details of the People to the caller 
999    
1000            return People;
1001            
1002        }
1003        
1004        /**
1005         * Return the names of all of the telescope operators to the
1006         * caller which are recalled from the fault database table
1007         * <b>TELESCOPEOPERATORVIEW</b>. 
1008         * <P>
1009         * 
1010         * @return ArrayList<Person> A array which contains objects of type
1011         * <b>Person</b>.
1012         * @throws UnableToGetListException If there is a problem getting
1013         * the list back from the database
1014         * @gwt.typeArgs <GWTApplication.client.Person> 
1015         */
1016        
1017        public ArrayList<Person> getTelescopeOperators(final boolean getOldStaff) throws UnableToGetListException {
1018            return getPersonDetails("TELESCOPEOPERATORVIEW",null,getOldStaff);
1019        }
1020    
1021        /**
1022         * Return the names of all of the duty technicians to the
1023         * caller which are recalled from the fault database table
1024         * <b>DUTYTECHNICIANVIEW</b>. 
1025         * <P>
1026         * @throws UnableToGetListException If there is a problem getting
1027         * the list back from the database     
1028         * @gwt.typeArgs <GWTApplication.client.Person> 
1029         * @return ArrayList<Person> A array which contains objects of type
1030         * <b>Person</b>.
1031         */
1032        
1033        public ArrayList<Person> getDutyTechnicians(final boolean getOldStaff) throws UnableToGetListException {
1034            return getPersonDetails("DUTYTECHNICIANVIEW",null,getOldStaff);
1035        }
1036    
1037        /**
1038         * Return the names of all of the support astronomers to the
1039         * caller which are recalled from the fault database table. 
1040         * <b>SUPPORTASTRONOMERVIEW</b>. 
1041         * @throws UnableToGetListException If there is a problem getting
1042         * the list back from the database     
1043         * <P>
1044         * 
1045         * @return ArrayList<Person> A array which contains objects of type
1046         * <b>Person</b>.
1047         * @gwt.typeArgs <GWTApplication.client.Person> 
1048         */
1049        
1050        public ArrayList<Person> getSupportAstronomers(final boolean getOldStaff) throws UnableToGetListException {
1051            return getPersonDetails("SUPPORTASTRONOMERVIEW",null,getOldStaff);
1052        }
1053    
1054        /**
1055         * Return the names of all of the staff members to the
1056         * caller which are recalled from the fault database table. 
1057         * <b>STAFFMEMBER</b>. 
1058         * @throws UnableToGetListException If there is a problem getting
1059         * the list back from the database
1060         * <P>
1061         * 
1062         * @return ArrayList<Person> A array which contains objects of type
1063         * <b>Person</b>.
1064         * @gwt.typeArgs <GWTApplication.client.Person> 
1065         */
1066        
1067        public ArrayList<Person> getStaffMembers(final boolean getOldStaff)  throws UnableToGetListException {
1068            return getPersonDetails("STAFFMEMBER",null,getOldStaff);
1069        }
1070    
1071        /**
1072         * Return the names of all of the sites to the
1073         * caller which are recalled from the fault database table. 
1074         * <b>SITES</b>. 
1075         * <P>
1076         * @throws UnableToGetListException If there is a problem getting
1077         * the list back from the database
1078         * @return ArrayList<Site> A array which contains objects of type
1079         * <b>Site</b>.
1080         * @gwt.typeArgs <GWTApplication.client.Site>
1081         */
1082        
1083        public ArrayList<Site> getSites () throws UnableToGetListException {
1084    
1085            final ArrayList<Site> Sites = new ArrayList<Site>();
1086    
1087            Connection connection  = null;
1088            Statement  stmt = null;
1089            ResultSet  rs   = null;
1090            
1091            if (dataSource == null) {
1092                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1093            }
1094                    
1095            // Now get a database connection from the connection pool if
1096            // we can
1097            
1098            try {
1099                
1100                Site newSite;
1101                
1102                connection = dataSource.getConnection();
1103                stmt       = connection.createStatement();
1104                
1105                rs = stmt.executeQuery("select * from site order by id");
1106    
1107                while (rs.next()) {
1108    
1109                    newSite = new Site();
1110    
1111                    newSite.setEn_description(rs.getString("en_description"));
1112                    newSite.setSp_description(rs.getString("sp_description"));
1113                    newSite.setId(rs.getString("id"));
1114    
1115                    Sites.add(newSite);
1116                }
1117    
1118                // Ensure that we clean up the database connection
1119                // correctly so that the connection is returned to the
1120                // database connection pool
1121    
1122                rs.close();
1123                connection.close();
1124                stmt.close();
1125                
1126                rs         = null;
1127                connection = null;
1128                stmt       = null;
1129    
1130            } catch (SQLException e) {
1131                e.printStackTrace();
1132                throw new UnableToGetListException("Unable to get list of sites : " + e.getMessage());
1133            } finally {
1134    
1135                // Always make sure result sets and statements are closed,
1136                // and the connection is returned to the pool
1137    
1138                closeDatabaseConnections(connection,stmt,rs);                   
1139            }
1140    
1141            // Now return the details of the Sites to the caller 
1142    
1143            return Sites;
1144        }
1145    
1146        /**
1147         * Return the names of all of the system to the
1148         * caller which are recalled from the fault database table. 
1149         * <b>SITES</b>. 
1150         * <P>
1151         * @throws UnableToGetListException If there is a problem getting
1152         * the list back from the database
1153         * @return ArrayList<FDBSystem> A array which contains objects of type
1154         * <b>System</b>.
1155         * @gwt.typeArgs <GWTApplication.client.FDBSystem>
1156         */
1157        
1158        public ArrayList<FDBSystem> getSystems () throws UnableToGetListException {
1159    
1160            final ArrayList<FDBSystem> Systems = new ArrayList<FDBSystem>();
1161    
1162            Connection connection  = null;
1163            Statement  stmt = null;
1164            ResultSet  rs   = null;
1165            
1166            if (dataSource == null) {
1167                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1168            }
1169                    
1170            // Now get a database connection from the connection pool if
1171            // we can
1172            
1173            try {
1174                
1175                FDBSystem newSystem;
1176                
1177                connection = dataSource.getConnection();
1178                stmt       = connection.createStatement();
1179                
1180                rs = stmt.executeQuery("select * from system order by id");
1181    
1182                while (rs.next()) {
1183    
1184                    newSystem = new FDBSystem();
1185    
1186                    newSystem.setEn_description(rs.getString("en_description"));
1187                    newSystem.setSp_description(rs.getString("sp_description"));
1188                    newSystem.setId(rs.getString("id"));
1189    
1190                    Systems.add(newSystem);
1191                }
1192    
1193                // Ensure that we clean up the database connection
1194                // correctly so that the connection is returned to the
1195                // database connection pool
1196    
1197                rs.close();
1198                connection.close();
1199                stmt.close();
1200                
1201                rs         = null;
1202                connection = null;
1203                stmt       = null;
1204    
1205            } catch (SQLException e) {
1206                e.printStackTrace();
1207                throw new UnableToGetListException("Unable to get list of systems : " + e.getMessage());
1208            } finally {
1209    
1210                // Always make sure result sets and statements are closed,
1211                // and the connection is returned to the pool
1212    
1213                closeDatabaseConnections(connection,stmt,rs);                   
1214            }
1215    
1216            // Now return the details of the Systems to the caller 
1217    
1218            return Systems;
1219        }
1220    
1221    
1222        /**
1223         * Return the names of all of the severity  to the
1224         * caller which are recalled from the fault database table. 
1225         * <b>SEVERITY</b>. 
1226         * <P>
1227         * @throws UnableToGetListException If there is a problem getting
1228         * the list back from the database
1229         * @return ArrayList<GWTApplication.client.Severity> A array which contains objects of type
1230         * <b>Severity</b>.
1231         * @gwt.typeArgs <GWTApplication.client.Severity>
1232         */
1233        
1234        public ArrayList<GWTApplication.client.Severity> getSeverity () throws UnableToGetListException {
1235    
1236            ArrayList<GWTApplication.client.Severity> Severity = new ArrayList<GWTApplication.client.Severity>();
1237    
1238            Connection connection  = null;
1239            Statement  stmt = null;
1240            ResultSet  rs   = null;
1241            
1242            if (dataSource == null) {
1243                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1244            }
1245                    
1246            // Now get a database connection from the connection pool if
1247            // we can
1248            
1249            try {
1250                
1251                GWTApplication.client.Severity newSeverity;
1252                
1253                connection = dataSource.getConnection();
1254                stmt       = connection.createStatement();
1255                
1256                rs = stmt.executeQuery("select * from severity order by id");
1257    
1258                while (rs.next()) {
1259    
1260                    newSeverity = new GWTApplication.client.Severity();
1261    
1262                    newSeverity.setEn_description(rs.getString("en_description"));
1263                    newSeverity.setSp_description(rs.getString("sp_description"));
1264                    newSeverity.setId(rs.getString("id"));
1265    
1266                    Severity.add(newSeverity);
1267                }
1268    
1269                // Ensure that we clean up the database connection
1270                // correctly so that the connection is returned to the
1271                // database connection pool
1272    
1273                rs.close();
1274                connection.close();
1275                stmt.close();
1276                
1277                rs         = null;
1278                connection = null;
1279                stmt       = null;
1280    
1281            } catch (SQLException e) {
1282                e.printStackTrace();
1283                throw new UnableToGetListException("Unable to get list of severities : " + e.getMessage());
1284            } finally {
1285    
1286                // Always make sure result sets and statements are closed,
1287                // and the connection is returned to the pool
1288    
1289                closeDatabaseConnections(connection,stmt,rs);                   
1290            }
1291    
1292            // Now return the details of the Severity to the caller 
1293    
1294            return Severity;
1295        }
1296    
1297    
1298        /**
1299         * Return the names of all of the instrument  to the
1300         * caller which are recalled from the fault database table. 
1301         * <b>INSTRUMENT</b>. 
1302         * <P>
1303         * @param allInstruments Whether to get all instruments or just
1304         * those which are not decommissioned.
1305         * @throws UnableToGetListException If there is a problem getting
1306         * the list back from the database
1307         * @return ArrayList<Instrument> A array which contains objects of type
1308         * <b>Instrument</b>.
1309         * @gwt.typeArgs <GWTApplication.client.Instrument>
1310         */
1311        
1312        public ArrayList<Instrument> getInstruments (final boolean allInstruments) throws UnableToGetListException {
1313    
1314            final ArrayList<Instrument> Instrument = new ArrayList<Instrument>();
1315    
1316            Connection connection  = null;
1317            Statement  stmt = null;
1318            ResultSet  rs   = null;
1319            
1320            if (dataSource == null) {
1321                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1322            }
1323                    
1324            // Now get a database connection from the connection pool if
1325            // we can
1326            
1327            try {
1328                
1329                Instrument newInstrument;
1330                
1331                connection = dataSource.getConnection();
1332                stmt       = connection.createStatement();
1333                
1334                if (allInstruments) {
1335                    rs = stmt.executeQuery("select * from instrument order by id");
1336                } else {
1337                    rs = stmt.executeQuery("select * from instrument where decommissioned = 'false' order by id");                          
1338                }
1339                
1340                while (rs.next()) {
1341    
1342                    newInstrument = new Instrument();
1343    
1344                    newInstrument.setEn_description(rs.getString("en_description"));
1345                    newInstrument.setSp_description(rs.getString("sp_description"));
1346                    newInstrument.setId(rs.getString("id"));
1347    
1348                    Instrument.add(newInstrument);
1349                }
1350    
1351                // Ensure that we clean up the database connection
1352                // correctly so that the connection is returned to the
1353                // database connection pool
1354    
1355                rs.close();
1356                connection.close();
1357                stmt.close();
1358                
1359                rs         = null;
1360                connection = null;
1361                stmt       = null;
1362    
1363            } catch (SQLException e) {
1364    
1365                e.printStackTrace();
1366    
1367                // Always make sure result sets and statements are closed,
1368                // and the connection is returned to the pool
1369                
1370                throw new UnableToGetListException("Unable to get list of instruments : " + e.getMessage());
1371    
1372            } finally {
1373    
1374                closeDatabaseConnections(connection,stmt,rs);                   
1375                
1376            }
1377    
1378            // Now return the details of the Instrument to the caller 
1379    
1380            return Instrument;
1381        }
1382        
1383        /**
1384         * Used to update the details of fault which has been modified by
1385         * the user. The details should have had their format verified by
1386         * the client and should be ready for simply updating the contents
1387         * in the database now.
1388         * @param FaultUpdateDetails This contains the details of the
1389         * changes that were made to the fault which need to be
1390         * re-inserted into the database.
1391         * @param locale This is the locale in which we are operating within 
1392         * @throws UnableToUpdateFaultException If the fault could not be
1393         * updated for whatever reason
1394         */
1395        
1396        public boolean updateFault(final Fault FaultUpdateDetails,final String locale) throws UnableToUpdateFaultException {
1397            
1398            logger.debug("Updating fault " + FaultUpdateDetails.id);
1399            
1400            Connection connection  = null;
1401            Statement  stmt        = null;
1402            ResultSet  rs          = null;
1403                
1404            // Now we need to build an SQL statement which can be used to
1405            // put the data into the fault table in the database
1406            
1407            final String updateStatement = "update fault set description = '" + makeTextFieldDatabaseCompatible(FaultUpdateDetails.description) + "',title = '" + makeTextFieldDatabaseCompatible(FaultUpdateDetails.title) + "', timeoccured = '" + FaultUpdateDetails.dateOccured + " " + FaultUpdateDetails.timeOccured + "',timelost = '" + FaultUpdateDetails.timeLost + "',telescopeoperator = '" + FaultUpdateDetails.telescopeOperatorEmail + "',site = '" + FaultUpdateDetails.site_id + "', observer = '" + FaultUpdateDetails.observer + "', system = '" + FaultUpdateDetails.system_id + "', instrument = '" + FaultUpdateDetails.instrument_id + "', dutytechnician = '" + FaultUpdateDetails.dutyTechnicianEmail + "', supportastronomer = '" + FaultUpdateDetails.supportAstronomerEmail + "',severity = '" + FaultUpdateDetails.severity_id + "', state = '" + FaultUpdateDetails.state_id + "', priority = " + FaultUpdateDetails.priority + ", faulttype = '" + FaultUpdateDetails.faultType_id + "', assignedto = '" + FaultUpdateDetails.assignedToEmail + "', lastmodified = 'NOW'  where id = " + FaultUpdateDetails.id;
1408            
1409            logger.debug( "Fault update statement >> " + updateStatement);
1410            
1411            if (dataSource == null) {
1412                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1413            }
1414                    
1415            // Now get a database connection from the connection pool if
1416            // we can to do the update 
1417            
1418            try {
1419                
1420                connection = dataSource.getConnection();
1421                stmt       = connection.createStatement();
1422                
1423                // If the person to which this fault is assigned is
1424                // specified then we need update the date when the fault
1425                // was assigned if it has not been set already. We only
1426                // update the assigned date once. 
1427                
1428                if (!FaultUpdateDetails.assignedToEmail.equals("UNKNOWN")) {
1429                    stmt.execute("update fault set assigneddate = 'now' where id = " + FaultUpdateDetails.id + " and assignedto = 'UNKNOWN'");
1430                }
1431                
1432                // Get the time of the last update for this fault and
1433                // ensure that we are updating the fault with out of date
1434                // information (i.e. somebody in the background as not
1435                // come along and updated the fault whilst we have been
1436                // updating the fault.
1437                
1438                rs = stmt.executeQuery("select lastmodified from fault where id = " + FaultUpdateDetails.id);
1439    
1440                if (rs.next()) {
1441                
1442                    final Timestamp currentTimeStamp = rs.getTimestamp("lastmodified");
1443    
1444                    final Timestamp lastModifiedTime = new Timestamp(FaultUpdateDetails.lastModified);
1445                    
1446                    if (currentTimeStamp.getTime() != lastModifiedTime.getTime()) {
1447                        logger.info("Fault " + FaultUpdateDetails.id + " has been changed by another user before we had the chance to save the update");
1448                        throw new UnableToUpdateFaultException("Fault has been updated/closed since you started editing the fault");
1449                    }           
1450    
1451                } else {
1452                    throw new UnableToUpdateFaultException("Unable to find the details of fault " + FaultUpdateDetails.id);
1453                }
1454                
1455                rs.close();
1456                    
1457                // Now update the fault if the fault has not already been updated in the background 
1458    
1459                stmt.execute(updateStatement);
1460                stmt.close();
1461                        
1462                connection.close();
1463                stmt.close();
1464                
1465                connection = null;
1466                stmt       = null;
1467    
1468            } catch (SQLException e) {
1469                e.printStackTrace();
1470                throw new UnableToUpdateFaultException("Unable to update the fault " + e.getMessage());
1471            } finally {
1472    
1473                // Always make sure result sets and statements are closed,
1474                // and the connection is returned to the pool
1475    
1476                closeDatabaseConnections(connection,stmt,rs);                   
1477            }
1478            
1479            return true;
1480        }
1481        
1482        /**
1483         * This method will be called to create a new fault in the fault
1484         * database from the data which should have been passed into the
1485         * method by AJAX client which should be running on the clients
1486         * web browser. All of the parameters should have been verified by the
1487         * client before they are sent down to this method so we will
1488         * not verify them again. 
1489         *
1490         * @param newFault This structure will contain the details which
1491         * are to be entered into the database when the fault is created. 
1492         * @throws UnableToCreateFaultException When the servlet is unable
1493         * to create the fault in the database it throws this exception.
1494         */
1495        
1496        public Integer createFault (final Fault newFault) throws UnableToCreateFaultException {
1497    
1498            Connection connection  = null;
1499            Statement  stmt        = null;
1500            ResultSet  rs          = null;
1501            
1502            int defectNumber = -9999;
1503    
1504            // Now we need to build an SQL statement which can be used to
1505            // put the data into the fault table in the database
1506            
1507            final String insertStatement = "insert into fault (description,title,timeoccured,timelost,telescopeoperator,site,observer,system,instrument,dutytechnician,supportastronomer,enteredby,severity,state,faultopen,lastmodified) values (" + 
1508                "'" + makeTextFieldDatabaseCompatible(newFault.description) + "'," + 
1509                "'" + makeTextFieldDatabaseCompatible(newFault.title) + "'," + 
1510                "'" + newFault.dateOccured + " " + newFault.timeOccured + "'," + 
1511                "'" + newFault.timeLost + "'," + 
1512                "'" + newFault.telescopeOperatorEmail + "'," + 
1513                "'" + newFault.site + "'," + 
1514                "'" + newFault.observer + "'," + 
1515                "'" + newFault.system + "'," + 
1516                "'" + newFault.instrument + "'," + 
1517                "'" + newFault.dutyTechnicianEmail + "'," + 
1518                "'" + newFault.supportAstronomerEmail + "'," + 
1519                "'" + newFault.enteredByEmail + "'," + 
1520                "'" + newFault.severity + "'," + 
1521                "'" + "NEW" + "'," + 
1522                "'true','NOW')";
1523    
1524            logger.debug( "Fault insertion statement >> " + insertStatement);
1525            
1526            if (dataSource == null) {
1527                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1528            }
1529                    
1530            // Now get a database connection from the connection pool if
1531            // we can
1532            
1533            try {
1534                
1535                connection = dataSource.getConnection();
1536                stmt       = connection.createStatement();
1537    
1538                stmt.execute(insertStatement);
1539                stmt.close();
1540                        
1541                // Now get defect number which was given to the fault so
1542                // that we can return it to the caller
1543    
1544                stmt = connection.createStatement();
1545                
1546                rs = stmt.executeQuery("select max(id) from fault");
1547                
1548                rs.next();
1549    
1550                defectNumber = rs.getInt(1);
1551                
1552                logger.debug("Defect number is " + defectNumber);
1553    
1554                // Now if the user has requested that he should be
1555                // informed when there is a change to the fault we make an
1556                // entry into the fault listener table. Note that we can
1557                // only do this if the user has authenticated himself with
1558                // his real user, not as a casual user such as observer. 
1559    
1560                if (newFault.userWantsEmailUpdates) {
1561                    
1562                    logger.debug(newFault.enteredByEmail);
1563    
1564                    if (!newFault.enteredByEmail.equalsIgnoreCase("UNKNOWN") && 
1565                        !newFault.enteredByEmail.equalsIgnoreCase(FaultDatabaseConstants.GUEST) &&
1566                        !newFault.enteredByEmail.equalsIgnoreCase("OBSERVER")) {
1567                        
1568                        // Now make an entry into the fault listener table
1569                        
1570                        stmt = connection.createStatement();
1571                        
1572                        stmt.execute("insert into faultlistener(email,id) values ('" + newFault.enteredByEmail + "'," + defectNumber + ")");
1573                        stmt.close();
1574                    }
1575                }
1576    
1577                // Ensure that we clean up the database connection
1578                // correctly so that the connection is returned to the
1579                // database connection pool
1580                
1581                rs.close();
1582                connection.close();
1583                stmt.close();
1584                
1585                rs         = null;
1586                connection = null;
1587                stmt       = null;
1588    
1589            } catch (SQLException e) {
1590                e.printStackTrace();
1591                
1592                throw new UnableToCreateFaultException("Unable to create fault : " + e.getMessage());
1593                
1594            } finally {
1595    
1596                // Always make sure result sets and statements are closed,
1597                // and the connection is returned to the pool
1598    
1599                closeDatabaseConnections(connection,stmt,rs);                   
1600            }
1601    
1602            // Now we need to send a SNMP message to indicate a new fault
1603            // has been raised by the system
1604            
1605            raiseSNMPAlert(defectNumber,newFault);
1606            
1607            return new Integer(defectNumber);
1608        }
1609        
1610        /**
1611         * This is user to raise an SMNP alert when a fault has been
1612         * raised in the system. 
1613         * <P>
1614         * In order to test this facility out one must use the SmnpRequest
1615         * class which comes the snmp4j package and set up an smtp
1616         * manager. This can be done with something like the following : 
1617         * <P>
1618         * <pre>java -cp /<Java path>/SNMP4J.jar org.snmp4j.tools.console.SnmpRequest -u aSecurityName -Ol 0.0.0.0/<port no>
1619         * </pre>
1620         * The port number is the port number which the SNMP manager is to
1621         * listen on. You will need to set the port in the init() method as
1622         * well to match the port which the manager is using. 
1623         * <P>
1624         * @param defectNumber This is the number of the defect which has been raised
1625         * @param fault The details of the fault which is to be entered.
1626         */
1627        
1628        void raiseSNMPAlert(final int defectNumber,final Fault fault) {
1629    
1630            // Setting up target
1631    
1632            CommunityTarget target = new CommunityTarget();
1633            target.setCommunity(new OctetString("public"));
1634            target.setAddress(targetAddress);
1635            target.setRetries(2);
1636            target.setTimeout(1500);
1637            target.setVersion(SnmpConstants.version2c);
1638    
1639            // Create the data packet which contains the details of the
1640            // alert to send to the SNMP manager
1641    
1642            PDU pdu = new PDU();
1643            pdu.add(new VariableBinding(new OID("1.3.6.1.4.777"), new OctetString("Fault " + Integer.valueOf(defectNumber) + " <" + fault.title + ">" + " at the " + fault.site + " (Time Lost " + fault.timeLost + " (HH:MM))" )));
1644            pdu.add(new VariableBinding(new OID("1.3.6.1.4.777"), new OctetString("Fault " + Integer.valueOf(defectNumber) + " <" + fault.title + ">" + " at the " + fault.site + " (Time Lost " + fault.timeLost + " (HH:MM))" )));
1645            pdu.add(new VariableBinding(new OID("1.3.6.1.4.777"), new OctetString("Fault " + Integer.valueOf(defectNumber) + " <" + fault.title + ">" + " at the " + fault.site + " (Time Lost " + fault.timeLost + " (HH:MM))" )));
1646    
1647            pdu.setType(PDU.TRAP);
1648    
1649            // Now send the TRAP to the SNMP manager to inform him that a
1650            // new fault has been created.
1651    
1652            try {
1653                snmp.send(pdu, target);
1654            } catch (IOException e) {
1655                logger.error("Unable to send SNMP trap" + e.getMessage());
1656            }
1657        }
1658        
1659        /**
1660         * Get all faults which go back to a specific date which has been
1661         * specified by the caller.
1662         * <P>
1663         * @param SearchType This is the type of search which is to be performed 
1664         * @param locale This is the locale which has been chosen by the user 
1665         * @return ArrayList<FaultType> This is an array of type fault which will
1666         * contain the details of all of the faults which were found as
1667         * part of the search. 
1668         */
1669        
1670        public ArrayList<Fault> getQuickViewFaultData (final int SearchType,final String locale) {
1671            
1672            logger.debug("Getting quick view information from the database");
1673    
1674            // First we need to create the SQL which we will use to get
1675            // the fault information back from the database
1676            
1677            StringBuffer selectString = new StringBuffer();
1678            
1679            String sectionAllocatedTo = null;
1680    
1681            String view                     = null;
1682            String whereClause              = " where ";
1683            boolean searchOutstandingFaults = false;
1684    
1685            final ArrayList<Fault> faultsFound    = new ArrayList<Fault>();             
1686            
1687            java.util.GregorianCalendar dateToSearchFrom = new java.util.GregorianCalendar();
1688            
1689            // Set the time to the current time 
1690            
1691            dateToSearchFrom.setTime(new java.util.Date());
1692            
1693            switch (SearchType) { 
1694                
1695            case QuickViewReport.TWENTYFOURHOURS :
1696                dateToSearchFrom.add(Calendar.DAY_OF_MONTH,-1);
1697                break;
1698                
1699            case QuickViewReport.SEVENTYTWOHOURS : 
1700                dateToSearchFrom.add(Calendar.DAY_OF_MONTH,-3);
1701                break;
1702                
1703            case QuickViewReport.ONEWEEK : 
1704                dateToSearchFrom.add(Calendar.DAY_OF_MONTH,-7);
1705                break;
1706                
1707            case QuickViewReport.ONEMONTH : 
1708                dateToSearchFrom.add(Calendar.MONTH,-1);
1709                break;
1710    
1711            case QuickViewReport.OUTSTANDING : 
1712                dateToSearchFrom = null;
1713                searchOutstandingFaults = true;
1714                break;
1715    
1716            case QuickViewReport.TI_OUTSTANDING : 
1717                dateToSearchFrom = null;
1718                searchOutstandingFaults = true;
1719                sectionAllocatedTo = "T&I";
1720                break;
1721    
1722            case QuickViewReport.COMPUTING_OUTSTANDING : 
1723                dateToSearchFrom = null;
1724                searchOutstandingFaults = true;
1725                sectionAllocatedTo = "COMPUTING";
1726                break;
1727    
1728            case QuickViewReport.OPERATIONS_OUTSTANDING : 
1729                dateToSearchFrom = null;
1730                searchOutstandingFaults = true;
1731                sectionAllocatedTo = "OPERATIONS";
1732                break;
1733    
1734            case QuickViewReport.ASTRONOMY_OUTSTANDING : 
1735                dateToSearchFrom = null;
1736                searchOutstandingFaults = true;
1737                sectionAllocatedTo = "ASTRONOMY";
1738                break;
1739                
1740            }
1741    
1742            // Depending on whether we are in spanish or english we search
1743            // across different database views.
1744    
1745            if (locale.equalsIgnoreCase("en")) {
1746                view = "faultview_en";
1747            } else {
1748                view = "faultview_sp";
1749            }
1750            
1751            selectString.append("select * from " + view);
1752            
1753            // Now put in the date filter into the select statement 
1754            
1755            final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
1756                     
1757            if (dateToSearchFrom != null) {
1758                selectString.append(" " + whereClause + " timeentered > '" + dateFormatter.format(dateToSearchFrom.getTime()) + "'");
1759                whereClause = " and ";          
1760            }
1761            
1762            // If we have been requested to display only oustanding faults
1763            // then we should only display faults which are new,pending or
1764            // inhand.
1765            
1766            if (searchOutstandingFaults) {
1767    
1768                selectString.append(" " + whereClause + " state_id in ('NEW','PENDING','INHAND')");
1769                
1770                if (sectionAllocatedTo != null) {
1771                    selectString.append(" and faulttype_id = '" + sectionAllocatedTo + "'");
1772                }
1773                    
1774                whereClause = " and ";
1775            }
1776                    
1777            selectString.append(" order by id desc limit  " + MAXNUMBEROFRESULTS);
1778            
1779            // Now get the data back from the database 
1780            
1781            Connection connection  = null;
1782            Statement  stmt = null;
1783            ResultSet  rs   = null;
1784                            
1785            // Now get a database connection from the connection pool if
1786            // we can
1787            
1788            if (dataSource == null) {
1789                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
1790            }
1791                    
1792            try {
1793    
1794                logger.debug("quick view select string is " + selectString.toString());
1795    
1796                connection = dataSource.getConnection();
1797                stmt       = connection.createStatement();
1798                rs         = stmt.executeQuery(selectString.toString());
1799                
1800                while (rs.next()) {
1801                    faultsFound.add(packFault(rs,true));
1802                }
1803    
1804                // Ensure that we clean up the database connection
1805                // correctly so that the connection is returned to the
1806                // database connection pool
1807    
1808                rs.close();
1809                connection.close();
1810                stmt.close();
1811                
1812                rs         = null;
1813                connection = null;
1814                stmt       = null;
1815    
1816            } catch (SQLException e) {
1817                e.printStackTrace();
1818            } finally {
1819    
1820                // Always make sure result sets and statements are closed,
1821                // and the connection is returned to the pool
1822    
1823                closeDatabaseConnections(connection,stmt,rs);                   
1824            }
1825            
1826            return faultsFound;
1827        }        
1828    
1829        
1830        /**
1831         * This will be simply used to pack the details of a fault as read
1832         * out of the database into the fault class. It can be configured
1833         * to work on either a result set from a fault view or the fault
1834         * table.
1835         * @throws SQLException If there is a problem reading the data from the database 
1836         * @param rs This is a result set which can be used to read the details of the fault which is to be packed. 
1837         * @param includeFullViewInformation Assumes that the result set
1838         * belongs to a view and that we should pack all of that
1839         * information as well.
1840         * @return Fault This is a instance of the fault object which contains the details of the fault. 
1841         * @gwt.typeArgs  <GWTApplication.client.Fault>
1842         */
1843        
1844        public Fault packFault (final ResultSet rs,final boolean includeFullViewInformation) throws SQLException {
1845             
1846             Fault fault = new Fault();     
1847    
1848             Timestamp temp                 = rs.getTimestamp("lastmodified");
1849             
1850             fault.lastModified             = temp.getTime();
1851             
1852             fault.id                       = rs.getInt("id");       
1853             fault.description              = rs.getString("description");
1854             fault.title                    = rs.getString("title");
1855             fault.timeLost                 = rs.getString("timelost");
1856             fault.observer                 = rs.getString("observer");
1857             fault.site                     = rs.getString("site");
1858             fault.severity                 = rs.getString("severity");
1859    
1860             fault.faultType                = rs.getString("faulttype");
1861    
1862             fault.instrument               = rs.getString("instrument");
1863    
1864             fault.priority                 = rs.getInt("priority");
1865    
1866             fault.state                    = rs.getString("state");
1867             fault.faultOpen                = rs.getBoolean("faultopen");
1868             fault.system                   = rs.getString("system");
1869    
1870             // If we have been requested to include all of the data from
1871             // the view then we should otherwise we only include the data
1872             // from the base table
1873    
1874             if (includeFullViewInformation) {
1875    
1876                 fault.assignedToEmail              = rs.getString("assignedto_email");
1877                 fault.enteredByEmail               = rs.getString("enteredby_email");
1878                 fault.dutyTechnicianEmail  = rs.getString("dutytechnician_email");
1879                 fault.supportAstronomerEmail       = rs.getString("sa_email");
1880                 fault.telescopeOperatorEmail       = rs.getString("to_email");
1881    
1882                 fault.severity_id                  = rs.getString("severity_id");
1883                 fault.faultType_id                 = rs.getString("faulttype_id");
1884                 fault.instrument_id                = rs.getString("instrument_id");
1885                 fault.site_id                      = rs.getString("site_id");
1886                 fault.state_id                     = rs.getString("state_id");
1887                 fault.system_id                    = rs.getString("system_id");
1888                     
1889                 fault.assignedToSurname            = rs.getString("assignedto_surname");
1890                 fault.assignedToName               = rs.getString("assignedto_name");
1891             
1892                 fault.enteredBySurname             = rs.getString("enteredby_surname");
1893                 fault.enteredByName                = rs.getString("enteredby_name");
1894             
1895                 fault.dutyTechnicianSurname        = rs.getString("dutytechnician_surname");
1896                 fault.dutyTechnicianName           = rs.getString("dutytechnician_name");
1897             
1898                 fault.supportAstronomerSurname     = rs.getString("sa_surname");
1899                 fault.supportAstronomerName        = rs.getString("sa_name"); 
1900             
1901                 fault.telescopeOperatorSurname     = rs.getString("to_surname");
1902                 fault.telescopeOperatorName        = rs.getString("to_name");       
1903    
1904             } else {
1905    
1906                 // The fields in the fault resultset are slightly
1907                 // different to those of a view
1908    
1909                 fault.assignedToEmail              = rs.getString("assignedto");
1910                 fault.enteredByEmail               = rs.getString("enteredby");
1911                 fault.dutyTechnicianEmail          = rs.getString("dutytechnician");
1912                 fault.supportAstronomerEmail       = rs.getString("supportastronomer");
1913                 fault.telescopeOperatorEmail       = rs.getString("telescopeoperator");         
1914    
1915             }
1916    
1917             // Now get the various dates & times and translate them back
1918             // to something that can be displayed by the web browser.
1919             
1920             java.sql.Timestamp  assignedTime, timeOccuredTime, enteredTime;
1921    
1922             final SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
1923             final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
1924             
1925             enteredTime     = rs.getTimestamp("timeentered");
1926             timeOccuredTime = rs.getTimestamp("timeoccured");
1927             assignedTime    = rs.getTimestamp("assigneddate");
1928    
1929             // If the entered time is specified then strip out the time
1930             // and the date
1931    
1932             if (enteredTime != null) {
1933                 fault.timeEntered = timeFormatter.format(enteredTime);
1934                 fault.dateEntered = dateFormatter.format(enteredTime);
1935             }
1936             
1937             if (timeOccuredTime != null) {
1938                 fault.timeOccured = timeFormatter.format(timeOccuredTime);
1939                 fault.dateOccured = dateFormatter.format(timeOccuredTime);
1940             }
1941             
1942             if (assignedTime != null) {
1943                 fault.assignedDate = dateFormatter.format(assignedTime);
1944             }
1945    
1946             // Get the total amount spent on this fault 
1947             
1948             fault.totalTimeSpentOnFault = calculateTotalAmountOfTimeSpentOnFault(fault.id);
1949    
1950             return fault;
1951         }
1952    
1953        /**
1954         * This will be simply used to pack the details of a linked fault
1955         * into a fault structure. This is <b> not </b> the same as the
1956         * packFault method as we are packing the information from the
1957         * fault tabl and not a fault view. This is for reasons of
1958         * performance as the SQL to get the details of the links from the
1959         * database which was working across the views was taking 3
1960         * seconds to perform which was just unacceptable to such a
1961         * critical section of code. 
1962         * @throws SQLException If there is a problem reading the data from the database 
1963         * @param rs This is a result set which can be used to read the details of the fault which is to be packed. 
1964         * @return Fault This is a instance of the fault object which contains the details of the fault. 
1965         * @gwt.typeArgs  <GWTApplication.client.Fault>
1966         */
1967        
1968        private Fault packLink (final ResultSet rs) throws SQLException {
1969             
1970             Fault fault = new Fault();     
1971    
1972             Timestamp temp                 = rs.getTimestamp("lastmodified");
1973    
1974             fault.lastModified             = temp.getTime();
1975             
1976             fault.id                       = rs.getInt("id");       
1977             fault.description              = rs.getString("description");
1978             fault.title                    = rs.getString("title");
1979             fault.timeLost                 = rs.getString("timelost");
1980             fault.observer                 = rs.getString("observer");
1981             fault.site_id                  = rs.getString("site");
1982             fault.severity_id              = rs.getString("severity");
1983             fault.faultType_id             = rs.getString("faulttype");
1984             fault.instrument_id            = rs.getString("instrument");
1985             fault.priority                 = rs.getInt("priority");
1986             fault.state_id                 = rs.getString("state");
1987             fault.faultOpen                = rs.getBoolean("faultopen");
1988             fault.system_id                = rs.getString("system");
1989             fault.assignedToEmail          = rs.getString("assignedto");
1990             fault.enteredByEmail           = rs.getString("enteredby");
1991             fault.dutyTechnicianEmail      = rs.getString("dutytechnician");
1992             fault.supportAstronomerEmail   = rs.getString("supportastronomer");
1993             fault.telescopeOperatorEmail   = rs.getString("telescopeoperator");
1994    
1995             // Now get the various dates & times and translate them back
1996             // to something that can be displayed by the web browser.
1997             
1998             java.sql.Timestamp  assignedTime, timeOccuredTime, enteredTime;
1999    
2000             final SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
2001             final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
2002             
2003             enteredTime     = rs.getTimestamp("timeentered");
2004             timeOccuredTime = rs.getTimestamp("timeoccured");
2005             assignedTime    = rs.getTimestamp("assigneddate");
2006    
2007             // If the entered time is specified then strip out the time
2008             // and the date
2009    
2010             if (enteredTime != null) {
2011                 fault.timeEntered = timeFormatter.format(enteredTime);
2012                 fault.dateEntered = dateFormatter.format(enteredTime);
2013             }
2014             
2015             if (timeOccuredTime != null) {
2016                 fault.timeOccured = timeFormatter.format(timeOccuredTime);
2017                 fault.dateOccured = dateFormatter.format(timeOccuredTime);
2018             }
2019             
2020             if (assignedTime != null) {
2021                 fault.assignedDate = dateFormatter.format(assignedTime);
2022             }
2023    
2024             // Get the total amount spent on this fault 
2025             
2026             fault.totalTimeSpentOnFault = calculateTotalAmountOfTimeSpentOnFault(fault.id);
2027             
2028             return fault;
2029         }
2030    
2031        /**
2032         * Return the names of all of the FaultType  to the
2033         * caller which are recalled from the fault database table. 
2034         * <b>FAULTTYPE</b>. 
2035         * <P>
2036         * @throws UnableToGetListException If there is a problem getting
2037         * the list back from the database
2038         * @return ArrayList<FaultType> A array which contains objects of type
2039         * <b>FaultType</b>.
2040         * @gwt.typeArgs <GWTApplication.client.FaultType>
2041         */
2042        
2043        public ArrayList<FaultType> getFaultType () throws UnableToGetListException {
2044    
2045            ArrayList<FaultType> FaultType = new ArrayList<FaultType>();
2046    
2047            Connection connection  = null;
2048            Statement  stmt = null;
2049            ResultSet  rs   = null;
2050            
2051            if (dataSource == null) {
2052                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2053            }
2054                    
2055            // Now get a database connection from the connection pool if
2056            // we can
2057            
2058            try {
2059                
2060                FaultType newFaultType;
2061                
2062                connection = dataSource.getConnection();
2063                stmt       = connection.createStatement();
2064                
2065                rs = stmt.executeQuery("select * from FaultType order by id");
2066    
2067                while (rs.next()) {
2068    
2069                    newFaultType = new FaultType();
2070    
2071                    newFaultType.setEn_description(rs.getString("en_description"));
2072                    newFaultType.setSp_description(rs.getString("sp_description"));
2073                    newFaultType.setId(rs.getString("id"));
2074    
2075                    FaultType.add(newFaultType);
2076                }
2077    
2078                // Ensure that we clean up the database connection
2079                // correctly so that the connection is returned to the
2080                // database connection pool
2081    
2082                rs.close();
2083                connection.close();
2084                stmt.close();
2085                
2086                rs         = null;
2087                connection = null;
2088                stmt       = null;
2089    
2090            } catch (SQLException e) {
2091                e.printStackTrace();
2092                throw new UnableToGetListException("Unable to get list of fault types : " + e.getMessage());        
2093                
2094            } finally {
2095    
2096                // Always make sure result sets and statements are closed,
2097                // and the connection is returned to the pool
2098    
2099                closeDatabaseConnections(connection,stmt,rs);                   
2100                
2101            }
2102    
2103            // Now return the details of the FaultType to the caller 
2104    
2105            return FaultType;
2106        }
2107    
2108        /**
2109         * Return the names of all of the states to the
2110         * caller which are recalled from the fault database table. 
2111         * <b>STATES</b>. 
2112         * <P>
2113         * @throws UnableToGetListException If there is a problem getting
2114         * the list back from the database
2115         * @return ArrayList<State> A array which contains objects of type
2116         * <b>State</b>.
2117         * @gwt.typeArgs <GWTApplication.client.State>
2118         */
2119        
2120        public ArrayList<State> getStates () throws UnableToGetListException {
2121    
2122            ArrayList<State> States = new ArrayList<State>();
2123    
2124            Connection connection  = null;
2125            Statement  stmt = null;
2126            ResultSet  rs   = null;
2127            
2128            if (dataSource == null) {
2129                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2130            }
2131                    
2132            // Now get a database connection from the connection pool if
2133            // we can
2134            
2135            try {
2136                
2137                State newState;
2138                
2139                connection = dataSource.getConnection();
2140                stmt       = connection.createStatement();
2141                
2142                rs = stmt.executeQuery("select * from state where id <> 'RELEASED' order by id");
2143    
2144                while (rs.next()) {
2145    
2146                    newState = new State();
2147    
2148                    newState.setEn_description(rs.getString("en_description"));
2149                    newState.setSp_description(rs.getString("sp_description"));
2150                    newState.setId(rs.getString("id"));
2151    
2152                    States.add(newState);
2153                }
2154    
2155                // Ensure that we clean up the database connection
2156                // correctly so that the connection is returned to the
2157                // database connection pool
2158    
2159                rs.close();
2160                connection.close();
2161                stmt.close();
2162                
2163                rs         = null;
2164                connection = null;
2165                stmt       = null;
2166    
2167            } catch (SQLException e) {
2168                e.printStackTrace();
2169                throw new UnableToGetListException("Unable to get list of : " + e.getMessage());    
2170            } finally {
2171    
2172                // Always make sure result sets and statements are closed,
2173                // and the connection is returned to the pool
2174                
2175                closeDatabaseConnections(connection,stmt,rs);                   
2176            }
2177    
2178            // Now return the details of the States to the caller 
2179    
2180            return States;
2181        }
2182    
2183        /**
2184         * Will be used to massage a text field into the correct format
2185         * for insertion into the database
2186         * 
2187         * @param InputString This is the string which is to be converted
2188         */
2189        
2190        private String makeTextFieldDatabaseCompatible (final String InputString) {
2191            
2192            String processedString;
2193            
2194            processedString = InputString.replaceAll("'","`");
2195            
2196            return processedString;
2197        }
2198    
2199    
2200        /**
2201         * Pack the details of a solution result set object into an
2202         * <b>Solution</b> object. 
2203         * @param rs This is a object which contains a database row which
2204         * contains the details of a solution.
2205         */
2206        
2207        private Solution packSolution (final ResultSet rs) throws SQLException {
2208            
2209            Solution solution = new Solution();
2210            
2211            // Now get the various dates & times and translate them back
2212            // to something that can be displayed by the web browser.
2213             
2214            java.util.Date timeOfInitialEntry;
2215    
2216            final SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
2217            final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
2218            
2219            solution.enteredByName    = rs.getString("name");
2220            solution.enteredBySurname = rs.getString("surname");
2221            solution.timeSpent        = rs.getString("timespent");
2222            solution.enteredByEmail   = rs.getString("email");
2223            solution.id               = rs.getInt("id");    
2224            solution.description      = rs.getString("description");
2225    
2226            timeOfInitialEntry        = rs.getDate("timeentered");
2227            
2228            // Convert the time of entry into a human readable form 
2229    
2230            if (timeOfInitialEntry != null) {
2231                solution.timeEntered = timeFormatter.format(timeOfInitialEntry);
2232                solution.dateEntered = dateFormatter.format(timeOfInitialEntry);
2233            }
2234            
2235            return solution;
2236        }
2237    
2238        /**
2239         * This method is called to update the solution which is
2240         * associated with the fault in the database 
2241         */
2242        
2243        public void updateSolutionDetails (final Solution NewSolutionDetails) {
2244            
2245            // First we need to know if the solution already exists in the
2246            // database. If it does not exist then we need to perform an
2247            // insert otherwise we will need to perform an update
2248            
2249            Connection connection  = null;
2250            Statement  stmt = null;
2251            ResultSet  rs   = null;
2252                            
2253            // Now get a database connection from the connection pool if
2254            // we can
2255            
2256            if (dataSource == null) {
2257                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2258            }
2259            
2260            try {
2261    
2262                boolean performUpdate;
2263    
2264                // Establish if there is already a solution in the
2265                // database and if there is then we need to update the
2266                // details of the solution
2267    
2268                connection = dataSource.getConnection();
2269                stmt       = connection.createStatement();
2270                rs         = stmt.executeQuery("select * from solutionview where id = " + NewSolutionDetails.id);
2271                
2272                if (rs.next()) {
2273                    performUpdate = true;
2274                } else {
2275                    performUpdate = false;                          
2276                }
2277            
2278                // Now create the update/insert statement to update the
2279                // details of solution in the database
2280                
2281                final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
2282    
2283                String sqlStatement;
2284    
2285                if (performUpdate) {
2286                    sqlStatement = "update solution set enteredby = '" + NewSolutionDetails.enteredByEmail + "',description = '" +  makeTextFieldDatabaseCompatible(NewSolutionDetails.description) + "', timeentered = '" + dateFormatter.format(new java.util.Date()) + "',timespent = '" + NewSolutionDetails.timeSpent + "' where id = " + NewSolutionDetails.id;
2287                } else {
2288                    sqlStatement = "insert into solution (enteredby,description,id,timespent) values ('" + NewSolutionDetails.enteredByEmail + "','" + makeTextFieldDatabaseCompatible(NewSolutionDetails.description) + "'," +  NewSolutionDetails.id + ",'" + NewSolutionDetails.timeSpent + "')";
2289                }
2290    
2291                logger.debug("Solution sql statement is " + sqlStatement);
2292                
2293                // Now update the database with the details of the solution 
2294    
2295                stmt.execute(sqlStatement);
2296                
2297                // Now update the time that the fault was last modified 
2298                
2299                stmt.execute("update fault set lastmodified = 'NOW' where id = " + NewSolutionDetails.id);
2300                
2301                // Ensure that we clean up the database connection
2302                // correctly so that the connection is returned to the
2303                // database connection pool
2304    
2305                rs.close();
2306                connection.close();
2307                stmt.close();
2308                
2309                rs         = null;
2310                connection = null;
2311                stmt       = null;
2312    
2313            } catch (SQLException e) {
2314                e.printStackTrace();
2315            } finally {
2316    
2317                // Always make sure result sets and statements are closed,
2318                // and the connection is returned to the pool
2319    
2320                closeDatabaseConnections(connection,stmt,rs);                   
2321            }
2322        }
2323        
2324        /**
2325         * Get the details of a solution from the database which is
2326         * associated with the fault number which was specified by the
2327         * caller
2328         * <P>
2329         * @param DefectNumber This is the number of the fault which is
2330         * associated with the solution which we are to return to the
2331         * caller
2332         * @return Solution This structure contains the details of the solution if there is one
2333         */
2334        
2335        public Solution getSolutionDetails (final Integer DefectNumber) {
2336    
2337            Solution solution = null;
2338                   
2339            // Now get the data back from the database 
2340            
2341            Connection connection  = null;
2342            Statement  stmt = null;
2343            ResultSet  rs   = null;
2344                            
2345            // Now get a database connection from the connection pool if
2346            // we can
2347            
2348            if (dataSource == null) {
2349                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2350            }
2351                    
2352            try {
2353    
2354                connection = dataSource.getConnection();
2355                stmt       = connection.createStatement();
2356                rs         = stmt.executeQuery("select * from solutionview where id = " + DefectNumber.toString());
2357                
2358                if (rs.next()) {
2359                    solution = packSolution(rs);
2360                }
2361    
2362                //log("select * from solution view where id = " + DefectNumber.toString());
2363    
2364                // Ensure that we clean up the database connection
2365                // correctly so that the connection is returned to the
2366                // database connection pool
2367    
2368                rs.close();
2369                connection.close();
2370                stmt.close();
2371                
2372                rs         = null;
2373                connection = null;
2374                stmt       = null;
2375    
2376            } catch (SQLException e) {
2377                e.printStackTrace();
2378            } finally {
2379    
2380                // Always make sure result sets and statements are closed,
2381                // and the connection is returned to the pool
2382    
2383                closeDatabaseConnections(connection,stmt,rs);                   
2384            }
2385            
2386            return solution;
2387        }        
2388    
2389        /**
2390         * This method is called to update the workaround which is
2391         * associated with the fault in the database 
2392         */
2393        
2394        public void updateWorkaroundDetails (final Workaround NewWorkaroundDetails) {
2395            
2396            // First we need to know if the workaround already exists in the
2397            // database. If it does not exist then we need to perform an
2398            // insert otherwise we will need to perform an update
2399            
2400            Connection connection  = null;
2401            Statement  stmt = null;
2402            ResultSet  rs   = null;
2403                            
2404            // Now get a database connection from the connection pool if
2405            // we can
2406            
2407            if (dataSource == null) {
2408                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2409            }
2410            
2411            try {
2412    
2413                boolean performUpdate;
2414    
2415                // Establish if there is already a workaround in the
2416                // database and if there is then we need to update the
2417                // details of the workaround
2418    
2419                connection = dataSource.getConnection();
2420                stmt       = connection.createStatement();
2421                rs         = stmt.executeQuery("select * from workaround where id = " + NewWorkaroundDetails.id);
2422                
2423                if (rs.next()) {
2424                    performUpdate = true;
2425                } else {
2426                    performUpdate = false;                          
2427                }
2428            
2429                // Now create the update/insert statement to update the
2430                // details of workaround in the database
2431                
2432                final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
2433    
2434                String sqlStatement;
2435    
2436                if (performUpdate) {
2437                    sqlStatement = "update workaround set enteredby = '" + NewWorkaroundDetails.enteredByEmail + "',description = '" +  makeTextFieldDatabaseCompatible(NewWorkaroundDetails.description) + "', timeentered = '" + dateFormatter.format(new java.util.Date()) + "',timespent = '" + NewWorkaroundDetails.timeSpent + "' where id = " + NewWorkaroundDetails.id;
2438                } else {
2439                    sqlStatement = "insert into workaround (enteredby,description,id,timespent) values ('" + NewWorkaroundDetails.enteredByEmail + "','" + makeTextFieldDatabaseCompatible(NewWorkaroundDetails.description) + "'," +  NewWorkaroundDetails.id + ",'" + NewWorkaroundDetails.timeSpent + "')";
2440                }
2441    
2442                logger.debug("Workaround sql statement is " + sqlStatement);
2443                
2444                // Now update the database with the details of the workaround 
2445    
2446                stmt.execute(sqlStatement);
2447                
2448                stmt.execute("update fault set lastmodified = 'NOW' where id = " + NewWorkaroundDetails.id);
2449                
2450                // Ensure that we clean up the database connection
2451                // correctly so that the connection is returned to the
2452                // database connection pool
2453    
2454                rs.close();
2455                connection.close();
2456                stmt.close();
2457                
2458                rs         = null;
2459                connection = null;
2460                stmt       = null;
2461    
2462            } catch (SQLException e) {
2463                e.printStackTrace();
2464            } finally {
2465    
2466                // Always make sure result sets and statements are closed,
2467                // and the connection is returned to the pool
2468    
2469                closeDatabaseConnections(connection,stmt,rs);                   
2470            }
2471        }
2472    
2473        
2474        /**
2475         * Get the details of a workaround from the database which is
2476         * associated with the fault number which was specified by the
2477         * caller
2478         * <P>
2479         * @param DefectNumber This is the number of the fault which is
2480         * associated with the workaround which we are to return to the
2481         * caller
2482         * @return Workaround This structure contains the details of the workaround if there is one
2483         */
2484        
2485        public Workaround getWorkaroundDetails (final Integer DefectNumber) {
2486            
2487            Workaround workaround = null;
2488                   
2489            // Now get the data back from the database 
2490            
2491            Connection connection  = null;
2492            Statement  stmt = null;
2493            ResultSet  rs   = null;
2494                            
2495            // Now get a database connection from the connection pool if
2496            // we can
2497            
2498            if (dataSource == null) {
2499                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2500            }
2501                    
2502            try {
2503    
2504                connection = dataSource.getConnection();
2505                stmt       = connection.createStatement();
2506                rs         = stmt.executeQuery("select * from workaroundview where id = " + DefectNumber.toString());
2507                
2508                if (rs.next()) {
2509                    workaround = packWorkaround(rs);
2510                }
2511    
2512                // Ensure that we clean up the database connection
2513                // correctly so that the connection is returned to the
2514                // database connection pool
2515    
2516                rs.close();
2517                connection.close();
2518                stmt.close();
2519                
2520                rs         = null;
2521                connection = null;
2522                stmt       = null;
2523    
2524            } catch (SQLException e) {
2525                e.printStackTrace();
2526            } finally {
2527    
2528                // Always make sure result sets and statements are closed,
2529                // and the connection is returned to the pool
2530    
2531                closeDatabaseConnections(connection,stmt,rs);                   
2532    
2533            }
2534            
2535            return workaround;
2536        }        
2537    
2538        /**
2539         * Pack the details of a workaround result set object into an
2540         * <b>Workaround</b> object. 
2541         * @param rs This is a object which contains a database row which
2542         * contains the details of a workaround.
2543         */
2544        
2545        private Workaround packWorkaround (final ResultSet rs) throws SQLException {
2546            
2547            Workaround workaround = new Workaround();
2548            
2549            // Now get the various dates & times and translate them back
2550            // to something that can be displayed by the web browser.
2551             
2552            java.util.Date timeOfInitialEntry;
2553    
2554            final SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
2555            final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
2556            
2557            workaround.enteredByName          = rs.getString("name");
2558            workaround.enteredBySurname       = rs.getString("surname");
2559            workaround.timeSpent              = rs.getString("timespent");
2560            workaround.enteredByEmail         = rs.getString("enteredby");
2561            workaround.id                     = rs.getInt("id");    
2562            workaround.description            = rs.getString("description");
2563    
2564            timeOfInitialEntry        = rs.getDate("timeentered");
2565            
2566            // Convert the time of entry into a human readable form 
2567    
2568            if (timeOfInitialEntry != null) {
2569                workaround.timeEntered = timeFormatter.format(timeOfInitialEntry);
2570                workaround.dateEntered = dateFormatter.format(timeOfInitialEntry);
2571            }
2572            
2573            return workaround;
2574        }
2575    
2576        /**
2577         * This method is called to update the comment which is associated
2578         * with the fault in the database
2579         * @param NewCommentDetails The details of the new comment. 
2580         */
2581        
2582        public void updateCommentDetails (final Comment NewCommentDetails) {
2583            
2584            // First we need to know if the comment already exists in the
2585            // database. If it does not exist then we need to perform an
2586            // insert otherwise we will need to perform an update
2587            
2588            Connection connection  = null;
2589            Statement  stmt = null;
2590                            
2591            // Now get a database connection from the connection pool if
2592            // we cang
2593            
2594            if (dataSource == null) {
2595                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2596            }
2597            
2598            try {
2599    
2600                // Establish if there is already a comment in the
2601                // database and if there is then we need to update the
2602                // details of the comment
2603    
2604                connection = dataSource.getConnection();
2605                stmt       = connection.createStatement();
2606                
2607                // Now create the update/insert statement to update the
2608                // details of comment in the database
2609                
2610                final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
2611    
2612                String sqlStatement;
2613    
2614                sqlStatement = "insert into comment (enteredby,description,id,timespent) values ('" + NewCommentDetails.enteredByEmail + "','" + makeTextFieldDatabaseCompatible(NewCommentDetails.description) + "'," +  NewCommentDetails.id + ",'" + NewCommentDetails.timeSpent + "')";
2615    
2616                logger.debug("Comment sql statement is " + sqlStatement);
2617                
2618                // Now update the database with the details of the comment 
2619    
2620                stmt.execute(sqlStatement);
2621                
2622                stmt.execute("update fault set lastmodified = 'NOW' where id = " + NewCommentDetails.id);
2623                
2624                // Ensure that we clean up the database connection
2625                // correctly so that the connection is returned to the
2626                // database connection pool
2627    
2628                connection.close();
2629                stmt.close();
2630                
2631                connection = null;
2632                stmt       = null;
2633    
2634            } catch (SQLException e) {
2635                e.printStackTrace();
2636            } finally {
2637    
2638                // Always make sure result sets and statements are closed,
2639                // and the connection is returned to the pool
2640    
2641                closeDatabaseConnections(connection,stmt,null);                 
2642            }
2643            
2644            calculateTotalAmountOfTimeSpentOnFault(NewCommentDetails.id);
2645        }
2646        
2647        /**
2648         * Get the details of a comment from the database which is
2649         * associated with the fault number which was specified by the
2650         * caller
2651         * <P>
2652         * @param DefectNumber This is the number of the fault which is
2653         * associated with the comment which we are to return to the
2654         * caller
2655         * @return ArrayList<Comment> This structure contains the details of all of
2656         * the comments which are associated with the fault
2657         * @gwt.typeArgs <GWTApplication.client.Comment>        
2658         */
2659        
2660        public ArrayList<Comment> getCommentDetails (final Integer DefectNumber) {
2661            
2662            ArrayList<Comment> comment = new ArrayList<Comment>();
2663                   
2664            // Now get the data back from the database 
2665            
2666            Connection connection  = null;
2667            Statement  stmt = null;
2668            ResultSet  rs   = null;
2669                            
2670            // Now get a database connection from the connection pool if
2671            // we can
2672            
2673            if (dataSource == null) {
2674                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2675            }
2676                    
2677            try {
2678    
2679                connection = dataSource.getConnection();
2680                stmt       = connection.createStatement();
2681                rs         = stmt.executeQuery("select * from commentview where id = " + DefectNumber.toString() + " order by timeentered");
2682                
2683                while (rs.next()) {
2684                    comment.add(packComment(rs));
2685                }
2686    
2687                // Ensure that we clean up the database connection
2688                // correctly so that the connection is returned to the
2689                // database connection pool
2690    
2691                rs.close();
2692                connection.close();
2693                stmt.close();
2694                
2695                rs         = null;
2696                connection = null;
2697                stmt       = null;
2698    
2699            } catch (SQLException e) {
2700                e.printStackTrace();
2701            } finally {
2702    
2703                // Always make sure result sets and statements are closed,
2704                // and the connection is returned to the pool
2705    
2706                closeDatabaseConnections(connection,stmt,rs);                   
2707            }
2708            
2709            return comment;
2710        }        
2711    
2712        /**
2713         * Pack the details of a comment result set object into an
2714         * <b>Comment</b> object. 
2715         * @param rs This is a object which contains a database row which
2716         * contains the details of a comment.
2717         */
2718        
2719        private Comment packComment (final ResultSet rs) throws SQLException {
2720            
2721            Comment comment = new Comment();
2722            
2723            // Now get the various dates & times and translate them back
2724            // to something that can be displayed by the web browser.
2725             
2726            java.util.Date timeOfInitialEntry;
2727    
2728            final SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm");
2729            final SimpleDateFormat dateFormatter = new SimpleDateFormat("dd/MM/yyyy");
2730            
2731            comment.enteredByName             = rs.getString("name");
2732            comment.enteredBySurname          = rs.getString("surname");
2733            comment.timeSpent                 = rs.getString("timespent");
2734            comment.enteredByEmail            = rs.getString("enteredby");
2735            comment.id                        = rs.getInt("id");    
2736            comment.description               = rs.getString("description");
2737    
2738            timeOfInitialEntry                = rs.getDate("timeentered");
2739            
2740            // Convert the time of entry into a human readable form 
2741    
2742            if (timeOfInitialEntry != null) {
2743                comment.timeEntered = timeFormatter.format(timeOfInitialEntry);
2744                comment.dateEntered = dateFormatter.format(timeOfInitialEntry);
2745            }
2746            
2747            return comment;
2748        }
2749    
2750        /**
2751         * This operation will be used to authenticate the user. If
2752         * authentication is successful then then level of priviledge will be
2753         * returned to the caller otherwise an exception will be raised.
2754         * @param UserIdentifier This is the user identifier of the user which is essentially his email address 
2755         * @param Password This is the password of the user 
2756         * @return AuthenticationDetails The level of priviledge and other
2757         * data relating to the user which was logged on
2758         * @throws UserAuthenticationFailedException Unable to authenticate the user. 
2759         */
2760    
2761        public AuthenticationDetails authenticateUser (String UserIdentifier, String Password) throws UserAuthenticationFailedException {
2762            
2763            // This will be used to hold the details 
2764    
2765            final AuthenticationDetails authenticationDetails = new AuthenticationDetails();
2766            
2767            // Now we need to look in the database and find out if the
2768            // user exists in the database and if so what is his level of
2769            // priviledge.
2770            
2771            logger.debug("Authenticating user " + UserIdentifier);
2772    
2773            Connection connection  = null;
2774            Statement  stmt        = null;
2775            ResultSet  rs          = null;
2776                            
2777            // Check to see if the user is attempting to log on without
2778            // credentials and if so give him the basic functionality, we
2779            // use the default password to log onto the system though
2780    
2781            if (UserIdentifier.equalsIgnoreCase(FaultDatabaseConstants.GUEST)) {
2782                Password       = "blackadder";
2783                UserIdentifier = FaultDatabaseConstants.GUEST;
2784            }
2785    
2786            // Now get a database connection from the connection pool if
2787            // we can
2788            
2789            if (dataSource == null) {
2790                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2791            }
2792                    
2793            boolean authenticated = false;
2794    
2795            try {
2796    
2797                connection = dataSource.getConnection();
2798                stmt       = connection.createStatement();
2799    
2800                rs = stmt.executeQuery("select * from staffmember where username = '" + UserIdentifier + "' and password ='" + Password + "' and stillwithing = true");
2801                
2802                // If there is a row in the table for that user and
2803                // password then we pack the details of the authentication
2804                // into the return value
2805    
2806                if (rs.next()) {
2807    
2808                    authenticated = true;
2809    
2810                    authenticationDetails.UserEmailAddress = rs.getString("email");
2811                    authenticationDetails.UserId           = rs.getString("username");
2812                    authenticationDetails.Name             = rs.getString("name");
2813                    authenticationDetails.Surname          = rs.getString("surname");
2814                } 
2815    
2816                rs.close();
2817                        
2818                // Now get the levels of the priviledge which are
2819                // associated with this user from the database
2820                
2821                rs = stmt.executeQuery("select * from roles where username = '" + UserIdentifier + "'");
2822    
2823                logger.debug("select * from roles where username = '" + UserIdentifier + "'");
2824    
2825                while (rs.next()) {
2826                    authenticationDetails.PriviledgeLevels.add(rs.getString("rolename"));
2827                    logger.debug("user " + UserIdentifier + " has the role " + rs.getString("rolename"));
2828                } 
2829    
2830                // Ensure that we clean up the database connection
2831                // correctly so that the connection is returned to the
2832                // database connection pool
2833    
2834                rs.close();
2835                connection.close();
2836                stmt.close();
2837                
2838                rs         = null;
2839                connection = null;
2840                stmt       = null;
2841    
2842            } catch (SQLException e) {
2843                e.printStackTrace();
2844            } finally {
2845    
2846                // Always make sure result sets and statements are closed,
2847                // and the connection is returned to the pool
2848    
2849                closeDatabaseConnections(connection,stmt,rs);                   
2850            }
2851    
2852            // If we were unable to authenticate the user then throw an exception 
2853            
2854            if (!authenticated) {
2855                throw new UserAuthenticationFailedException("Unable to authenticate user " + UserIdentifier);
2856            }
2857    
2858            logger.debug("User " + UserIdentifier + " has been authenticated");
2859            
2860            return authenticationDetails;
2861        }
2862    
2863        /**
2864         * Send an email to all of the people that have registered an
2865         * interest in this fault. The details of the fault are recalled
2866         * from the database and then sent out as a HTML or a text based
2867         * message depending on the user preferences.
2868         * @param ContextMessage This abstract text which is associated with the fault 
2869         * @param FaultNumber This is the identifier of the fault which
2870         * needs to be sent out to the user.
2871         */
2872        
2873    
2874        public void informEmailListenersFaultHasChanged (final String ContextMessage, final int FaultNumber) {
2875            
2876            Connection connection  = null;
2877            Statement  stmt        = null;
2878            ResultSet  rs          = null;
2879                            
2880            // Now get a database connection from the connection pool if
2881            // we can
2882            
2883            if (dataSource == null) {
2884                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
2885            }
2886                    
2887            // Now get a list of all of the listeners of this fault which
2888            // we are to send the details of the fault to
2889    
2890            ArrayList<String> faultListeners = new ArrayList<String>();
2891            
2892            try {
2893    
2894                connection = dataSource.getConnection();
2895                stmt       = connection.createStatement();
2896    
2897                rs = stmt.executeQuery("select * from faultlistener where id = " + Integer.toString(FaultNumber));
2898                
2899                while (rs.next()) {
2900                    faultListeners.add(rs.getString("email"));
2901                } 
2902    
2903                rs.close();
2904                connection.close();
2905                stmt.close();
2906                
2907                rs         = null;
2908                connection = null;
2909                stmt       = null;
2910    
2911            } catch (SQLException e) {
2912                e.printStackTrace();
2913            } finally {
2914    
2915                // Always make sure result sets and statements are closed,
2916                // and the connection is returned to the pool
2917    
2918                closeDatabaseConnections(connection,stmt,rs);                   
2919    
2920            }
2921    
2922            // Get a report of the fault 
2923            
2924            String faultReport = "Unknown";
2925            
2926            try {
2927                faultReport = fetchHTMLFaultReport(FaultNumber,false);
2928            } catch (UnknownFaultSpecifiedException e) {
2929                logger.error("Unable to find fault " + FaultNumber + " in the database " + e.getMessage());
2930            }
2931            
2932            // Now for each of listeners, tell them about the change to
2933            // the fault
2934    
2935            for (int i=0; i < faultListeners.size(); i++) { 
2936                
2937                String emailAddress = (String)faultListeners.get(i);
2938    
2939                try {
2940                    
2941                    javax.mail.Session session = javax.mail.Session.getInstance(props, null);
2942                    
2943                    Message msg = new MimeMessage(session);
2944    
2945                    msg.setFrom(new InternetAddress("faultdbadmin@ing.iac.es","Fault Database"));
2946                    
2947                    msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(emailAddress, false));
2948                    
2949                    msg.setSubject("Fault " + FaultNumber + " has been updated ");
2950                    
2951                    msg.setContent("You have been sent this email by the fault management system because you registered an interest in this fault, please do not reply to this message.<P>" + 
2952                                   "The reason you have been sent this email is because : <b>" + ContextMessage + 
2953                                   "</b><p>Click <b><a href=\"http://www.ing.iac.es:8081/faultdb/?EditFault=" + FaultNumber + "\">here</a></b> if you wish to update the fault.<P> " + 
2954                                   faultReport, "text/html");
2955    
2956                    msg.setHeader("X-Mailer", "Fault Management System");
2957    
2958                    msg.setSentDate(new java.util.Date());
2959                    
2960                    // Now send the message to the email address which was
2961                    // specified and, alas poor email, off it goes to the
2962                    // spam folder.
2963                    
2964                    Transport.send(msg);
2965                    
2966                } catch (Exception e) {
2967                    logger.error("Unable to send fault listener update message to " + emailAddress + " " + e.getMessage());
2968                }
2969            }
2970        }
2971         
2972        /**
2973         * Get a list of staff members which are not in the group people
2974         * which have been specified by the caller from the database. this
2975         * will be used for instance to get a list of all of the staff
2976         * members except those in the TO group for instance.
2977         *<P>
2978         * @param GroupToExclude This is the group of people which are to
2979         * be exceluded from the list of staff returned to the
2980         * caller. This parameter should be one of
2981         * <ul> 
2982         * <li> FaultDatabaseConstants.TELESCOPEOPERATOR
2983         * <li> FaultDatabaseConstants.DUTYENGINEERS
2984         * <li> FaultDatabaseConstants.SUPPORTASTRONOMERS
2985         * </ul>
2986         * <P>
2987         * @return ArrayList<Person> An array of class Person of the people in the
2988         * staff member list which are not in the group which was
2989         * specified.
2990         * @throws UnableToGetListException In the event that there
2991         * is a problem getting the list of people.
2992         * @gwt.typeArgs <GWTApplication.client.Person> 
2993         */
2994        
2995        public ArrayList<Person> getMembersOfStaffNotInGroup (final Integer GroupToExclude) throws UnableToGetListException {
2996            
2997            ArrayList<Person> PersonList = null;
2998            
2999            switch (GroupToExclude.intValue())  { 
3000                
3001            case FaultDatabaseConstants.TELESCOPEOPERATOR : 
3002                PersonList = getPersonDetails("STAFFMEMBER","TELESCOPEOPERATORVIEW",false);
3003                break;
3004    
3005            case FaultDatabaseConstants.DUTYENGINEERS : 
3006                PersonList = getPersonDetails("STAFFMEMBER","DUTYTECHNICIANVIEW",false);
3007                break;
3008                
3009            case FaultDatabaseConstants.SUPPORTASTRONOMERS : 
3010                PersonList = getPersonDetails("STAFFMEMBER","SUPPORTASTRONOMERVIEW",false);
3011                break;
3012    
3013            default : 
3014                logger.error("Group to exclude has been incorrectly specified" + GroupToExclude);
3015            }
3016            
3017            return PersonList;
3018        }
3019    
3020        /**
3021         * Will be used to add a new instrument to the database. 
3022         * @param newInstrument The name of the new instrument 
3023         * @throws UnableToGetPeopleListException If there was a problem
3024         * adding the new instrument to the list
3025         */
3026        
3027        public void addNewInstrumentToSystem (final String newInstrument) throws UnableToAddInstrumentException {
3028            
3029            Connection connection  = null;
3030            Statement  stmt        = null;
3031            ResultSet  rs          = null;
3032            
3033            // Now we need to build an SQL statement which can be used to
3034            // put the data into the fault table in the database
3035            
3036            final String insertStatement = "insert into instrument (id,sp_description,en_description) values ('" + newInstrument + "','" + newInstrument + "','" + newInstrument + "')";
3037    
3038            logger.debug( "Instrument insertion statement >> " + insertStatement);
3039            
3040            // Now get a database connection from the connection pool if
3041            // we can
3042            
3043            if (dataSource == null) {
3044                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3045            }
3046                            
3047            try {
3048                
3049                connection = dataSource.getConnection();
3050                stmt       = connection.createStatement();
3051    
3052                stmt.execute(insertStatement);
3053                
3054                connection.close();
3055                stmt.close();
3056                
3057                connection = null;
3058                stmt       = null;
3059    
3060            } catch (SQLException e) {
3061                e.printStackTrace();
3062                throw new UnableToAddInstrumentException("Database error : " + e.getMessage());
3063            } finally {
3064    
3065                // Always make sure result sets and statements are closed,
3066                // and the connection is returned to the pool
3067    
3068                closeDatabaseConnections(connection,stmt,null);                 
3069            }
3070    
3071            return; 
3072        }
3073    
3074        /**
3075         * Will be used to add a new system to the system. 
3076         * @param newSystem The name of the new system 
3077         * @throws UnableToGetPeopleListException If there was a problem
3078         * adding the new system to the list
3079         */
3080        
3081        public void addNewSystemToSystem (String newSystem) throws UnableToAddSystemException {
3082            
3083            Connection connection  = null;
3084            Statement  stmt        = null;
3085            
3086            // Now we need to build an SQL statement which can be used to
3087            // put the data into the fault table in the database
3088            
3089            final String insertStatement = "insert into system (id,sp_description,en_description) values ('" + newSystem + "','" + newSystem + "','" + newSystem + "')";
3090    
3091            logger.debug( "System insertion statement >> " + insertStatement);
3092            
3093            // Now get a database connection from the connection pool if
3094            // we can
3095            
3096            if (dataSource == null) {
3097                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3098            }
3099                            
3100            try {
3101                
3102                connection = dataSource.getConnection();
3103                stmt       = connection.createStatement();
3104    
3105                stmt.execute(insertStatement);
3106                
3107                connection.close();
3108                stmt.close();
3109                
3110                connection = null;
3111                stmt       = null;
3112    
3113            } catch (SQLException e) {
3114                e.printStackTrace();
3115                throw new UnableToAddSystemException("Database error : " + e.getMessage());
3116            } finally {
3117    
3118                // Always make sure result sets and statements are closed,
3119                // and the connection is returned to the pool
3120    
3121                closeDatabaseConnections(connection,stmt,null);                 
3122            }
3123        }
3124        
3125        /**
3126         * Will be used to add a new user to the role which is
3127         * specified. For example the caller will be able to add a user to
3128         * the list of telescope operators
3129         * @param emailAddress This is the email address of the person who is to be added to the people in the role which is specified 
3130         * @param Role This is the role into which the user will be added. Will be one of 
3131         * <ul>
3132         * <li> TELESCOPEOPERATOR
3133         * <li> DUTYENGINEER
3134         * <li> SUPPORTASTRONOMER
3135         * </ul>
3136         * @throws UnableToAddNewRoleException If there is a problem adding the user to the role which was specified 
3137         */
3138        
3139        public void addNewRole (final String emailAddress,final Integer Role) throws UnableToAddNewRoleException {
3140    
3141            Connection connection  = null;
3142            Statement  stmt        = null;
3143            
3144            String databaseTable;
3145            
3146            // Now we need to build an SQL statement which can be used to
3147            // put the data into the fault table in the database
3148            
3149            switch (Role.intValue()) {
3150                
3151            case FaultDatabaseConstants.TELESCOPEOPERATOR : 
3152                databaseTable = "TELESCOPEOPERATOR";
3153                break;
3154    
3155            case FaultDatabaseConstants.DUTYENGINEERS : 
3156                databaseTable = "DUTYTECHNICIAN";               
3157                break;
3158    
3159            case FaultDatabaseConstants.SUPPORTASTRONOMERS : 
3160                databaseTable = "SUPPORTASTRONOMER";
3161                break;
3162                
3163            default : 
3164                logger.error("Unknown role has been specified " + Role); 
3165                throw new UnableToAddNewRoleException("Unknown role specified " + Role);
3166            }
3167    
3168            final String insertStatement = "insert into " + databaseTable + "(email) values  ('" + emailAddress + "')";
3169            
3170            logger.debug( "System insertion statement >> " + insertStatement);
3171            
3172            // Now get a database connection from the connection pool if
3173            // we can
3174            
3175            if (dataSource == null) {
3176                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3177            }
3178                            
3179            try {
3180                
3181                connection = dataSource.getConnection();
3182                stmt       = connection.createStatement();
3183    
3184                stmt.execute(insertStatement);
3185                
3186                stmt.close();
3187                connection.close();
3188                
3189                connection = null;
3190                stmt       = null;
3191    
3192            } catch (SQLException e) {
3193                e.printStackTrace();
3194                throw new UnableToAddNewRoleException();
3195    
3196            } finally {
3197    
3198                // Always make sure result sets and statements are closed,
3199                // and the connection is returned to the pool
3200    
3201                closeDatabaseConnections(connection,stmt,null);                 
3202            }
3203        }
3204    
3205        /**
3206         * Will add the details of a new user to the system. 
3207         * @param Name This is the christian name of the user
3208         * @param Surname This is the surname of the user
3209         * @param EmailAddress This is the email address of the user 
3210         * @throws UnableToAddNewUserException If there was a problem
3211         * adding the details of the new user to the system
3212         */
3213        
3214        public void addNewUser(final String Name, final String Surname, final String EmailAddress) throws UnableToAddNewUserException {
3215            
3216            Connection connection  = null;
3217            Statement  stmt        = null;
3218            
3219            // We need to get the user name of the user which should be
3220            // established by stripping the userid from the email address
3221    
3222            final String[] emailAddressComponents =  EmailAddress.split("@");
3223            
3224            // Check that we were able to find a @ in the email address
3225    
3226            if (emailAddressComponents.length != 2) {
3227                throw new UnableToAddNewUserException();
3228            }
3229            
3230            // Now we need to create the SQL statement which we will use
3231            // to insert the data into the database
3232            
3233            String insertStatement = "insert into staffmember (surname,name,stillwithing,email,username) values ('" + makeTextFieldDatabaseCompatible(Surname) + "','" + Name + 
3234                "',true,'" + makeTextFieldDatabaseCompatible(EmailAddress) + "','" + makeTextFieldDatabaseCompatible(emailAddressComponents[0]) + "')";
3235    
3236            logger.debug(insertStatement);
3237            
3238            // Now get a database connection from the connection pool if
3239            // we can
3240    
3241            if (dataSource == null) {
3242                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3243            }
3244                                    
3245            try {
3246                
3247                connection = dataSource.getConnection();
3248                stmt       = connection.createStatement();
3249    
3250                stmt.execute(insertStatement);
3251                        
3252                // Now we need to add a new role into the role table for the new user 
3253                
3254                stmt.execute("insert into roles (rolename,username) values ('ing','" + makeTextFieldDatabaseCompatible(emailAddressComponents[0]) + "')");
3255    
3256                stmt.close();
3257                connection.close();
3258    
3259                connection = null;
3260                stmt       = null;
3261    
3262            } catch (SQLException e) {
3263                e.printStackTrace();
3264                throw new UnableToAddNewUserException("Database error : " + e.getMessage());
3265            } finally {
3266    
3267                // Always make sure result sets and statements are closed,
3268                // and the connection is returned to the pool
3269    
3270                closeDatabaseConnections(connection,stmt,null);                 
3271                
3272            }
3273    
3274            return;
3275        }
3276        
3277        /**
3278         * Allow the password associated with the user which is provided
3279         * to be changed to the demand password
3280         * @param emailAddress This is the email address of the user to have his password changed 
3281         * @param confirmedPassword This is the new password of the user 
3282         * @throws UnableToChangePasswordException When unable to update the password of the user 
3283         */
3284        
3285        public void resetPassword (final String emailAddress, final String confirmedPassword) throws UnableToChangePasswordException {
3286            
3287            Connection connection  = null;
3288            Statement  stmt        = null;
3289            
3290            String databaseTable;
3291            
3292            final String updateStatement = "update staffmember set password = '" + makeTextFieldDatabaseCompatible(confirmedPassword) + "' where email = '" + makeTextFieldDatabaseCompatible(emailAddress) + "'";
3293            
3294            logger.debug( "password update statement >> " + updateStatement);
3295            
3296            // Now get a database connection from the connection pool if
3297            // we can
3298            
3299            if (dataSource == null) {
3300                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3301            }
3302                            
3303            try {
3304                
3305                connection = dataSource.getConnection();
3306                stmt       = connection.createStatement();
3307    
3308                stmt.execute(updateStatement);
3309                
3310                stmt.close();
3311                connection.close();
3312                
3313                connection = null;
3314                stmt       = null;
3315    
3316            } catch (SQLException e) {
3317                e.printStackTrace();
3318                throw new UnableToChangePasswordException("Database error : " + e.getMessage());
3319    
3320            } finally {
3321    
3322                // Always make sure result sets and statements are closed,
3323                // and the connection is returned to the pool
3324    
3325                closeDatabaseConnections(connection,stmt,null);                 
3326            }
3327    
3328        }
3329        
3330        /**
3331         * Will be used to execute a basic search across the database
3332         * using the input data which has been provided. The user will
3333         * simply specify a string of search terms possibly using
3334         * wildcards and then this routine will attempt to match any
3335         * faults which may contain any of the search terms. The method
3336         * will search through <b>all</b> of the text fields as well as
3337         * the description and the comment fields in the fault.
3338         * <p>
3339         * @param DefectNumber This is the defect number for which we
3340         * should return the  fault information. 
3341         * @param SearchTerms A list of search terms which will be used to
3342         * index the fault information. 
3343         * @param DefectToSearchFor This is the defect number to search for if specified. 
3344         * @param StartDate This is the start date from when to look
3345         * from. This could be left blank. 
3346         * @param EndDate This is the end date from when to stop looking
3347         * for faults. 
3348         * @gwt.typeArgs <GWTApplication.client.FaultType>
3349         */
3350        
3351        public ArrayList<Fault> performBasicSearch (final int DefectNumber, 
3352                                                    final String SearchTerms, 
3353                                                    final String DefectToSearchFor,
3354                                                    String StartDate, 
3355                                                    String EndDate,
3356                                                    final String locale) throws UnableToPerformSearchException {
3357    
3358            String searchTerm = SearchTerms.trim();
3359            
3360            // Convert all of the * characters in the search term to %
3361            // symbols to be SQL compatible 
3362            
3363            searchTerm = searchTerm.replace('*','%');
3364            searchTerm = searchTerm.replace("_","\\_");
3365            searchTerm = searchTerm.replace("'","\\'");
3366            
3367            // Now break up the search string based on spaces and spaces
3368            // between quoted text. 
3369            
3370            final String[] searchItems = searchTerm.split("[ ]|\"[ ]?\"",0);
3371            
3372            // Now create a SQL statement which can be used to go through
3373            // the faults 
3374            
3375            String view;
3376            
3377            // Now depending on the locale we search different views 
3378            
3379            if (locale.equalsIgnoreCase("en")) {
3380                view = "faultview_en";
3381            } else {
3382                view = "faultview_sp";
3383            }
3384            
3385            // For each text field in the view we now create a search
3386            // across the fields. 
3387    
3388            StringBuilder sqlClause = new StringBuilder("select * from " + view + " where ");
3389            
3390            // If the user has specified the defect number then we simply
3391            // look for that defect number and ignore the rest of the
3392            // search terms. 
3393    
3394            boolean firstClauseInWhereStatement = true;         
3395                
3396            if (!DefectToSearchFor.equals("")) {
3397                sqlClause.append(" id = " + DefectToSearchFor);
3398    
3399            } else  {
3400    
3401                if (!searchTerm.equals("")) {
3402    
3403                    for (TextFieldsToSearch i : TextFieldsToSearch.values()) { 
3404    
3405                        String textFieldBooleanOperator;
3406                    
3407                        boolean firstSubClause = true;
3408    
3409                        // Whether to bracket the expression. If we start
3410                        // using the AND operator then we have to bracket the
3411                        // search otherwise the semantics are incorrect 
3412                        // i.e. a + b + c . e . f should be a + b + (c . e . f)
3413    
3414                        boolean bracketSubExpression = false;
3415    
3416                        // If the fields that we are searching is a pure text
3417                        // field such as the description or title field then
3418                        // we should perform an AND on these terms 
3419                    
3420                        if (i.equals(TextFieldsToSearch.CONJUGATEDTEXTFIELD)) {
3421                            textFieldBooleanOperator = " and ";
3422                            bracketSubExpression = true;
3423                        } else {
3424                            textFieldBooleanOperator = " or ";
3425                            bracketSubExpression = false;
3426                        }
3427                    
3428                        for (int j=0; j < searchItems.length; j++) { 
3429    
3430                            if (!firstClauseInWhereStatement) {
3431    
3432                                // In the description and title field, if we
3433                                // have more than one search term, we perform
3434                                // an AND. e.g if the user specifies "petals
3435                                // closing", then the search engine will match
3436                                // descriptions which have the words petals
3437                                // and closing in them. 
3438    
3439                                if (firstSubClause) {
3440                                    sqlClause.append(" or ");
3441                                } else {
3442                                    sqlClause.append(textFieldBooleanOperator);
3443                                }
3444                            
3445                            } else {
3446                                sqlClause.append("(");
3447                            }
3448                        
3449                            // If we are using AND with the text terms in the
3450                            // description and the title fields we should
3451                            // bracket the entire subexpression. 
3452    
3453                            if (firstSubClause && bracketSubExpression) {
3454                                sqlClause.append("(");
3455                                firstSubClause = false;
3456                            }
3457                        
3458                            sqlClause.append("lower(" + i.toString() + ") LIKE '%" + searchItems[j].toLowerCase() + "%'");
3459                    
3460                            // Establish if the user has entered an integer value for the
3461                            // search term and if he has then we have to try and match
3462                            // that value against a fault identifier
3463                        
3464                            try {
3465                                Integer.valueOf(searchTerm);
3466                            
3467                                // We managed to parse the integer so now add an SQL term
3468                                                    
3469                                sqlClause.append(" or id = " + searchTerm);
3470                            
3471                            } catch (NumberFormatException e) {
3472                                // Do nothing if the search term is not an integer. 
3473                            }
3474                        
3475                            firstClauseInWhereStatement = false;
3476                                            
3477                        }           
3478    
3479                        if (!firstSubClause && bracketSubExpression) {
3480                            sqlClause.append(")");
3481                        }
3482                    }
3483            
3484                    if (!firstClauseInWhereStatement) {
3485                        sqlClause.append(")");
3486                    }                   
3487                }
3488                
3489                // If the start date is before the end date then swap them
3490                // around
3491    
3492                if (!StartDate.equals("") && !EndDate.equals("")) {
3493                    
3494                    // If the user has specified both dates we need to ensure
3495                    // that the start date is before the end date and if not
3496                    // swap them around.
3497                    
3498                    final String [] endDateInFields = EndDate.split("/");
3499                
3500                    final String [] startDateInFields = StartDate.split("/");
3501                
3502                    final GregorianCalendar startDate,endDate;
3503    
3504                    try {
3505                        startDate = new GregorianCalendar(Integer.parseInt(startDateInFields[2]),Integer.parseInt(startDateInFields[1]),Integer.parseInt(startDateInFields[0]));
3506                        endDate       = new GregorianCalendar(Integer.parseInt(endDateInFields[2]),Integer.parseInt(endDateInFields[1]),Integer.parseInt(endDateInFields[0]));
3507                    } catch(NumberFormatException e) {
3508                        throw new UnableToPerformSearchException("Problem formatting date fields");
3509                    }
3510                
3511                    // If the start date is after the end date then swap them around 
3512    
3513                    if (startDate.compareTo(endDate) == 1) {
3514                    
3515                        final String temp = StartDate;
3516                    
3517                        StartDate = EndDate;
3518                        EndDate       = temp;
3519                    }
3520                }
3521                
3522                // If the user has specified a time window then we should add
3523                // that into the SQL as well. Note that we need to add one
3524                // day to the end date to ensure that we do an inclusive
3525                // search on the end date
3526    
3527                if (!StartDate.equals("")) {
3528                    
3529                    if (!firstClauseInWhereStatement) {
3530                        sqlClause.append(" and ");
3531                    }
3532                
3533                    firstClauseInWhereStatement = false;
3534                            
3535                    sqlClause.append(" timeentered >= '" + StartDate + "' ");
3536                }
3537    
3538                if (!EndDate.equals("")) {
3539                            
3540                    if (!firstClauseInWhereStatement) {
3541                        sqlClause.append(" and ");
3542                    }
3543    
3544                    firstClauseInWhereStatement = false;
3545                
3546                    sqlClause.append(" timeentered <= cast(cast ('" + EndDate + "' as date) + cast ('1 day' as interval) as date) ");
3547                }       
3548            }       
3549    
3550            sqlClause.append(" order by id desc limit  " + MAXNUMBEROFRESULTS);
3551    
3552            logger.debug("Search string is : " + sqlClause);
3553            
3554            // Now get the fault data back from the database 
3555            
3556            Connection connection  = null;
3557            Statement  stmt = null;
3558            ResultSet  rs   = null;
3559                            
3560            ArrayList<Fault> matchingFaults = new ArrayList<Fault>();
3561    
3562            // Now get a database connection from the connection pool if
3563            // we can
3564            
3565            if (dataSource == null) {
3566                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3567            }
3568                    
3569            try {
3570    
3571                connection = dataSource.getConnection();
3572                stmt       = connection.createStatement();
3573                rs         = stmt.executeQuery(sqlClause.toString());
3574                
3575                while (rs.next()) {
3576    
3577                    Fault faultData = packFault(rs,true);
3578                    matchingFaults.add(faultData);
3579                } 
3580    
3581                // Ensure that we clean up the database connection
3582                // correctly so that the connection is returned to the
3583                // database connection pool
3584    
3585                rs.close();
3586                connection.close();
3587                stmt.close();
3588                
3589                rs         = null;
3590                connection = null;
3591                stmt       = null;
3592    
3593            } catch (SQLException e) {
3594                e.printStackTrace();
3595                throw new UnableToPerformSearchException("Database error : " + e.getMessage());
3596            } finally {
3597               
3598                // Always make sure result sets and statements are closed,
3599                // and the connection is returned to the pool
3600    
3601                closeDatabaseConnections(connection,stmt,rs);                   
3602            }
3603    
3604            logger.debug("Simple Search completed");
3605    
3606            return matchingFaults;
3607        }
3608    
3609        /**
3610         * Will be used to link a fault which is specified by the user to another fault 
3611         * @param parentFaultId This is the parent fault which the child fault will be linked to
3612         * @param childFaultId  This the fault which is to be linked to the parent fault
3613         * @throws UnknownFaultSpecifiedException This exception is thrown if the fault which is specified by the user does not exist.
3614         */
3615        
3616        public void linkFault(final int parentFaultId, final int childFaultId) throws UnknownFaultSpecifiedException,Exception {
3617            
3618            logger.debug("Attempting to link " + childFaultId + " to " + parentFaultId);
3619    
3620            boolean unknownChildFaultIdSpecified = false;
3621    
3622            // Now find out if the fault which was specified by the user
3623            // actually exists in the database and otherwise we create an
3624            // error
3625    
3626            Connection connection  = null;
3627            Statement  stmt = null;
3628            ResultSet  rs   = null;
3629            
3630            if (dataSource == null) {
3631                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3632            }
3633                    
3634            // Now get a database connection from the connection pool if
3635            // we can
3636            
3637            try {
3638                        
3639                connection = dataSource.getConnection();
3640                stmt       = connection.createStatement();
3641                
3642                rs = stmt.executeQuery("select count(*) from fault where id = " + childFaultId);
3643                    
3644                if (rs.next()) {
3645                    
3646                    // There should be at least one fault which matches
3647                    // the fault id the user entered
3648                    
3649                    if (rs.getInt(1) != 1) {
3650                        unknownChildFaultIdSpecified = true;
3651                    }
3652                }
3653    
3654                // Now we need to insert a new entry into the fault link
3655                // table now.
3656                
3657                stmt.execute("insert into linkfault (childfault,parentfault) values (" + childFaultId + "," + parentFaultId + ")");
3658    
3659                // Now update the last modified time for the faults. 
3660    
3661                stmt.execute("update fault set lastmodified = 'NOW' where id = " + childFaultId + " or id = " + parentFaultId);
3662                
3663                stmt.close();
3664                
3665                // Ensure that we clean up the database connection
3666                // correctly so that the connection is returned to the
3667                // database connection pool
3668                
3669                rs.close();
3670                connection.close();
3671                stmt.close();
3672                
3673                rs         = null;
3674                connection = null;
3675                stmt       = null;
3676    
3677            } catch (SQLException e) {
3678                e.printStackTrace();
3679                throw new Exception("Unable to save fault link : " + e.getMessage());
3680            } finally {
3681    
3682                // Always make sure result sets and statements are closed,
3683                // and the connection is returned to the pool
3684    
3685                closeDatabaseConnections(connection,stmt,rs);                   
3686            }
3687    
3688            // If the user specified an unknown child fault id then throw an error 
3689    
3690            if (unknownChildFaultIdSpecified) {
3691                throw new UnknownFaultSpecifiedException();
3692            }
3693                        
3694            return;
3695        }
3696    
3697        
3698        /**
3699         * Gets all of the faults which are linked to the fault specified 
3700         * <P>
3701         * @param DefectNumber This is the number of the fault which we are to return to the caller 
3702         * @return ArrayList<Fault>  Array of all of the faults which are linked
3703         */
3704        
3705        public ArrayList<Fault> getLinkedFaults (final int DefectNumber) {
3706            
3707            //logger.debug("Getting linked faults to fault " + DefectNumber + " from the database");
3708    
3709            String view = null;
3710            
3711            ArrayList<Fault> linkedFaults = new ArrayList<Fault>();
3712                    
3713            // Now get the data back from the database 
3714            
3715            Connection connection  = null;
3716            Statement  stmt = null;
3717            ResultSet  rs   = null;
3718                            
3719            // Now get a database connection from the connection pool if
3720            // we can
3721            
3722            if (dataSource == null) {
3723                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3724            }
3725                    
3726            try {
3727    
3728                connection = dataSource.getConnection();
3729                stmt       = connection.createStatement();
3730                
3731                final String sqlStatement = "select  distinct * from fault  where id <> " + DefectNumber + " and id in (select childfault from linkfault where parentfault = " + DefectNumber + " union select parentfault from linkfault where childfault = " + DefectNumber + ")";
3732    
3733                rs         = stmt.executeQuery(sqlStatement);
3734                
3735                while (rs.next()) {
3736                    linkedFaults.add(packLink(rs));
3737                } 
3738    
3739                // Ensure that we clean up the database connection
3740                // correctly so that the connection is returned to the
3741                // database connection pool
3742    
3743                rs.close();
3744                connection.close();
3745                stmt.close();
3746                
3747                rs         = null;
3748                connection = null;
3749                stmt       = null;
3750    
3751            } catch (SQLException e) {
3752                e.printStackTrace();
3753            } finally {
3754               
3755                // Always make sure result sets and statements are closed,
3756                // and the connection is returned to the pool
3757    
3758                closeDatabaseConnections(connection,stmt,rs);                   
3759            }
3760            
3761            return linkedFaults;
3762        }        
3763        /**
3764         * Get the details of the fault from the database which was specified by the caller
3765         * <P>
3766         * @param DefectNumber This is the number of the fault which we are to return to the caller 
3767         * @return Fault This structure contains the details of the fault
3768         */
3769        
3770        public Fault getFaultDetails (final Integer DefectNumber,final String locale) {
3771            
3772            String view = null;
3773            
3774            Fault faultData = null;
3775            
3776            // Depending on whether we are in spanish or english we search
3777            // across different database views.
3778    
3779            if (locale.equalsIgnoreCase("en")) {
3780                view = "faultview_en";
3781            } else {
3782                view = "faultview_sp";
3783            }
3784            
3785            // Now get the data back from the database 
3786            
3787            Connection connection  = null;
3788            Statement  stmt = null;
3789            ResultSet  rs   = null;
3790                            
3791            // Now get a database connection from the connection pool if
3792            // we can
3793            
3794            if (dataSource == null) {
3795                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3796            }
3797                    
3798            try {
3799    
3800                connection = dataSource.getConnection();
3801                stmt       = connection.createStatement();
3802                rs         = stmt.executeQuery("select * from " + view + " where id = " + DefectNumber.toString());
3803                
3804                if (rs.next()) {
3805                    faultData = packFault(rs,true);
3806                } 
3807    
3808                //logger.debug(">>select * from " + view + " where id = " + DefectNumber.toString());
3809    
3810                // Ensure that we clean up the database connection
3811                // correctly so that the connection is returned to the
3812                // database connection pool
3813    
3814                rs.close();
3815                connection.close();
3816                stmt.close();
3817                
3818                rs         = null;
3819                connection = null;
3820                stmt       = null;
3821    
3822            } catch (SQLException e) {
3823                e.printStackTrace();
3824            } finally {
3825               
3826                // Always make sure result sets and statements are closed,
3827                // and the connection is returned to the pool
3828    
3829                closeDatabaseConnections(connection,stmt,rs);                   
3830            }
3831            
3832            return faultData;
3833        }        
3834        
3835        /** 
3836         * Get the total amount of time which has been spent on a
3837         * fault. This will sum the total amount of time which has been
3838         * allocated to comments, solutions and work arounds if they
3839         * exist
3840         * @param FaultNumber This is the fault number for which we
3841         * have to calculate the total time spent.
3842         * @return String The total amount of time spent in hours and minutes in the format HH:MM
3843         */
3844        
3845        public String calculateTotalAmountOfTimeSpentOnFault (final int FaultNumber) {
3846            
3847            int totalTimeSpentMins = 0;
3848    
3849            Connection connection  = null;
3850            Statement  stmt = null;
3851            ResultSet  rs   = null;
3852            
3853            // Now get a database connection from the connection pool if
3854            // we can
3855            
3856            if (dataSource == null) {
3857                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
3858            }
3859                    
3860            try {
3861    
3862                connection = dataSource.getConnection();
3863    
3864                stmt = connection.createStatement();
3865                rs = stmt.executeQuery("select coalesce(to_char(sum(timespent), 'HH24:MI'),'00:00') from comment where id = " + FaultNumber);
3866                rs.next();      
3867                totalTimeSpentMins += convertTimeToMinutes(rs.getString(1));
3868    
3869                rs.close();
3870                stmt.close();
3871                        
3872                // Now get the amount of time which has been spent on the solution
3873                    
3874                stmt = connection.createStatement();
3875                rs = stmt.executeQuery("select coalesce(to_char(sum(timespent), 'HH24:MI'),'00:00') from solution where id = " + FaultNumber);
3876                rs.next();
3877                totalTimeSpentMins += convertTimeToMinutes(rs.getString(1));
3878                
3879                rs.close();
3880                stmt.close();
3881    
3882                // Now get the amount of time which has been spent on the work around 
3883    
3884                stmt = connection.createStatement();
3885                rs = stmt.executeQuery("select coalesce(to_char(sum(timespent), 'HH24:MI'),'00:00') from workaround where id = " + FaultNumber);
3886                rs.next();
3887                totalTimeSpentMins += convertTimeToMinutes(rs.getString(1));
3888    
3889                rs.close();
3890                stmt.close();
3891    
3892                // Ensure that we clean up the database connection
3893                // correctly so that the connection is returned to the
3894                // database connection pool
3895                
3896                rs.close();
3897                connection.close();
3898                stmt.close();
3899                
3900                rs         = null;
3901                connection = null;
3902                stmt       = null;
3903    
3904            } catch (SQLException e) {
3905                e.printStackTrace();
3906            } finally {
3907               
3908                // Always make sure result sets and statements are closed,
3909                // and the connection is returned to the pool
3910    
3911                closeDatabaseConnections(connection,stmt,rs);                   
3912                
3913            }
3914    
3915            // Now convert the number of minutes to HH:MM
3916    
3917            return String.format("%1$02d:%2$02d",totalTimeSpentMins/60, totalTimeSpentMins % 60);
3918        }
3919    
3920        /**
3921         * Fetch from the database the details of the fault and all of
3922         * it's comments and prepare an HTML report which can be returned
3923         * to the user
3924         * @param faultId This is the identifier of the fault which is to be printed
3925         * @param addLinks Add the links which will allow user to log into the system and modify the fault 
3926         * @return String A string which contains a HTML based fault report 
3927         * @throws UnknownFaultSpecifiedException Unknown fault exception. 
3928         */
3929        
3930        public String fetchHTMLFaultReport (final int faultId, final boolean addLinks) throws UnknownFaultSpecifiedException {
3931            
3932            // Get the details of the fault from the database
3933            
3934            final Fault faultDetails = getFaultDetails (faultId,"en");
3935            
3936            if (faultDetails == null) {
3937                throw new UnknownFaultSpecifiedException("Fault " + faultId + " not found in the database");
3938            }
3939    
3940            final Solution solution = getSolutionDetails(faultId);
3941            
3942            final Workaround workAround = getWorkaroundDetails(faultId);
3943            
3944            // Now get all of the comments associated with the fault 
3945    
3946            final ArrayList<Comment> faultComments = getCommentDetails(faultId);
3947            
3948            final StringBuffer HTMLReport = new StringBuffer();
3949    
3950            HTMLReport.append
3951                ("<table cellpadding=3 cellspacing=1 width=\"95%\" border=\"Y\">" + 
3952                 "<tr><th bgcolor=\"#68A7C0\" colspan=6><a name=\"" + faultId + "\"> Fault " + faultId + "</a></th>");
3953    
3954            // Now we need to add the details of the links if specified
3955            // which will be used to allow the user to log onto the system
3956            // and modify the fault should it be required.
3957    
3958            HTMLReport.append("<tr><td bgcolor=\"#FFFFC9\" colspan=6><b>" + faultDetails.title + "</b>");
3959                 
3960            if (addLinks) {
3961    
3962                HTMLReport.append("<tr><th bgcolor=\"#E6EEFF\" align=left colspan=6><b>[<a href=\"http://www.ing.iac.es:8081/faultdb?EditFault=" + faultId + "\">Update</a></b>|");
3963                HTMLReport.append("<b><a href=\"#top\">Top</a></b>");
3964    
3965                // If the person assigned is known then put in a link
3966                // which will allow the user to email them
3967    
3968                if (!faultDetails.assignedToEmail.equalsIgnoreCase("UNKNOWN") && !faultDetails.assignedToEmail.equalsIgnoreCase(FaultDatabaseConstants.GUEST)) {
3969                    HTMLReport.append("|<b><a href=\"mailto:" + faultDetails.assignedToEmail + "?Subject=Fault Report " + faultId + "\">Contact Assignee</a></b>");
3970                }
3971                
3972                HTMLReport.append("]");
3973            }
3974    
3975            HTMLReport.append(
3976                "<tr><td><b>Time Occurred</b><td>" + faultDetails.timeOccured + "<td><b> Date Occurred</b><td>" + faultDetails.dateOccured + "<td><b>Entered By</b><td>" + faultDetails.enteredByName + " " + faultDetails.enteredBySurname + 
3977                 "<tr><td><b>Assigned to</b> <td>" + faultDetails.assignedToName + " " + faultDetails.assignedToSurname + "<td><b> Severity</b><td>" + faultDetails.severity + "<td><b>Site</b> <td>" + faultDetails.site + 
3978                 "<tr><td><b>Instrument </b><td>" + faultDetails.instrument + "<td><b> State</b><td>" + faultDetails.state + "<td><b>Type </b> <td>" + faultDetails.faultType + 
3979                 "<tr><th bgcolor=\"#E6EEFF\" colspan=6>Fault Description" + 
3980                 "<tr><td colspan=6>" + faultDetails.description);
3981            
3982            // Now for each of the comment which have been appended to the
3983            // fault add them into the report 
3984            
3985            for (int i=0; i < faultComments.size();i++) { 
3986                
3987                final Comment comment = faultComments.get(i);
3988    
3989                HTMLReport.append
3990                    ("<tr><th bgcolor=\"#E6EEFF\" colspan=6>Comment" + 
3991                     "<tr><td><b>Time entered</b><td>" + comment.timeEntered + "<td><b>Date Entered</b><td>" + comment.dateEntered + "<td><b>Entered By</b><td>" + comment.enteredByName + " " + comment.enteredBySurname + 
3992                     "<tr><td colspan=6>" + comment.description);
3993                
3994            }
3995    
3996            // If there is a solution then we add in the details of the solution 
3997            
3998            if (workAround != null) {
3999                HTMLReport.append
4000                    ("<tr><th bgcolor=\"#E6EEFF\" colspan=6>Workaround" +                
4001                     "<tr><td><b>Time entered</b><td>" + workAround.timeEntered + "<td><b>Date Entered</b><td>" + workAround.dateEntered + "<td><b>Entered By</b><td>" + workAround.enteredByName + " " + workAround.enteredBySurname + 
4002                     "<tr><td colspan=6>" + workAround.description);
4003    
4004            }
4005            
4006            // If there is a solution then we add in the details of the solution 
4007            
4008            if (solution != null) {
4009                HTMLReport.append
4010                    ("<tr><th bgcolor=\"#E6EEFF\" colspan=6>Solution" + 
4011                     "<tr><td><b>Time entered</b><td>" + solution.timeEntered + "<td><b>Date Entered</b><td>" + solution.dateEntered + "<td><b>Entered By</b><td>" + solution.enteredByName + " " + solution.enteredBySurname + 
4012                     "<tr><td colspan=6>" + solution.description);
4013    
4014            }
4015            
4016            HTMLReport.append("</table>");
4017            
4018            return HTMLReport.toString();
4019        
4020        }
4021        
4022        /**
4023         * Used to convert the HH:MM field into minutes expressed as
4024         * an integer. 
4025         * @param TimeAmount Time expressed in Hours:Minutes
4026         * @return int The amount expressed in minutes. 
4027         */
4028        
4029        private int convertTimeToMinutes(final String TimeAmount) {
4030            
4031            final String[] timeFields = TimeAmount.split(":");
4032            
4033            return (Integer.parseInt(timeFields[0]) * 60) + Integer.parseInt(timeFields[1]);
4034        }
4035    
4036        
4037        /**
4038         * Used to record in the database that the user would like to be
4039         * informed of updates on the fault
4040         * @param emailAddress this is the email address of the person that wants to recieve updates 
4041         * @param faultId This is the fault identifier of the fault that the wants to get updates for 
4042         */
4043        
4044        public void registerForFaultUpdates (final String emailAddress,final int faultId) {
4045            
4046            Connection connection  = null;
4047            Statement  stmt = null;
4048            
4049            // Now get a database connection from the connection pool if
4050            // we can
4051            
4052            if (dataSource == null) {
4053                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
4054            }
4055                    
4056            try {
4057    
4058                connection = dataSource.getConnection();
4059    
4060                stmt = connection.createStatement();
4061                stmt.execute("insert into faultlistener(email,id) values ('" + emailAddress + "'," + faultId + ")");
4062                stmt.close();
4063                        
4064                connection.close();
4065                stmt.close();
4066                
4067                connection = null;
4068                stmt       = null;
4069    
4070            } catch (SQLException e) {
4071                e.printStackTrace();
4072            } finally {
4073               
4074                // Always make sure result sets and statements are closed,
4075                // and the connection is returned to the pool
4076    
4077                closeDatabaseConnections(connection,stmt,null);                 
4078            }
4079        }
4080        
4081        /**
4082         * Will be used to print the fault specified on the printer which
4083         * has been specified. The method uses an external agent
4084         * <b>pdf2ps</b> to convert the PDF which is created into
4085         * postscript before it is printed. It is therefore important to
4086         * ensure that this process exists and is configured on the
4087         * machine which is hosting the fault database.
4088         * <P>
4089         * @param faultId This is the identifier of the fault to be printed 
4090         * @param printer This is the name of the printer which is to be used to print fault 
4091         * @param landscape True if the document is to be printed in landscape mode
4092         * @throws UnknownPrinterSpecifiedException If the printer that
4093         * was specified by the user does not exist
4094         * @return boolean True if we managed to print out the fault 
4095         */
4096        
4097        public boolean printFault (final int faultId,final String printer,boolean landscape) throws UnknownPrinterSpecifiedException {
4098    
4099            // These the temporary files which will be used by the system
4100    
4101            final String PSTEMPFILE = "/tmp/faultDBtempFile.ps";
4102            final String PDFTEMPFILE = "/tmp/PDFTEMPFILE.pdf";
4103            
4104            final Document PDFDocument;
4105    
4106            // Create the PDF document with the margins included. Check to
4107            // see if the user has requested the document in landscape or
4108            // portrait
4109    
4110            if (landscape) {
4111                PDFDocument = new Document(PageSize.A4.rotate(),5.0f,5.0f,90,40);
4112            } else {
4113                PDFDocument = new Document(PageSize.A4,5.0f,5.0f,90,40);                
4114            }
4115            
4116            // Verify that the printer that the user has specified
4117            // actually exists and if not then throw an error 
4118    
4119            final ArrayList<String> printerList = getPostscriptPrinters();
4120            
4121            boolean printerFound = false;
4122            
4123            for (int i=0; i < printerList.size();i++) { 
4124                
4125                final String currentPrinter = printerList.get(i);
4126                
4127                if (currentPrinter.equals(printer)) {
4128                    printerFound = true;
4129                    break;
4130                }
4131            }
4132    
4133            // If we could not find the printer that was specifed by the
4134            // user then we throw an error
4135    
4136            if (!printerFound) {
4137    
4138                logger.error("User has specified an unknown printer for this machine " + printer);
4139                
4140                throw new UnknownPrinterSpecifiedException("User has specified an unknown printer " + printer);
4141            }
4142    
4143            try {
4144                
4145                PdfWriter.getInstance (PDFDocument, new FileOutputStream (PDFTEMPFILE));
4146    
4147                PDFDocument.open();
4148                
4149                // Now add the fault to the PDF document which we are
4150                // creating
4151                
4152                createFaultReportAsPDF(faultId,PDFDocument);
4153                
4154            } catch (UnknownFaultSpecifiedException e) {
4155                logger.error("Unable to find fault " + faultId + " in the database");
4156                return false;
4157            } catch (Exception e) {
4158                logger.error(e.getMessage());
4159                return false;
4160            } finally {
4161                if (PDFDocument != null) {
4162                    PDFDocument.close();
4163                }
4164            }
4165            
4166            // Now convert the PDF file to postscript and then print it out ta-da! 
4167            
4168            final Runtime pdf2psConvertor = Runtime.getRuntime();
4169            
4170            try {
4171                
4172                final String[] cmd = {
4173                    "/usr/bin/pdf2ps",
4174                    PDFTEMPFILE,
4175                    PSTEMPFILE
4176                };
4177                
4178                final Process proc = pdf2psConvertor.exec(cmd);
4179                
4180                // Wait for the process to complete
4181                proc.waitFor();
4182                
4183                if (proc.exitValue() != 0) {
4184                    logger.error("Unable to convert the PDF to postscript");
4185                    return false;
4186                }
4187                
4188            } catch (Exception e) {
4189                logger.error("Unable to convert the PDF to postscript : " + e.getMessage());
4190                return false;
4191            }
4192            
4193            try {
4194            
4195                // Find the all of the print services which can print
4196                // postscript and then try and find the one that the user
4197                // specified.
4198    
4199                PrintService printServiceToUse = null;
4200                
4201                for (PrintService i : PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.POSTSCRIPT,null)) {
4202    
4203                    if (i.getName().equals(printer)) {
4204                        logger.info("Printing " + faultId + " on printer " + i.getName());
4205                        printServiceToUse = i;
4206                        break;
4207                    }
4208                }
4209            
4210                // If we could not find the name of the printer that the
4211                // user has specified then complain
4212    
4213                if (printServiceToUse == null) {
4214                    throw new UnknownPrinterSpecifiedException("User has specified an unknown printer " + printer);                     
4215                }
4216                
4217                // Create the print job
4218    
4219                final File psFile = new File(PSTEMPFILE);
4220                final DocPrintJob job = printServiceToUse.createPrintJob();
4221                
4222                final Doc doc = new SimpleDoc(psFile.toURL(),DocFlavor.URL.AUTOSENSE,null);
4223                
4224                // Now print out the document on the printer which was
4225                // specified by the caller
4226                
4227                job.print(doc,null);
4228                
4229            } catch (Exception e) {
4230                e.printStackTrace();
4231                return false;
4232            }
4233            
4234            return true;
4235        }
4236        
4237        /**
4238         * Add the full details of the fault history into the PDF document
4239         * ready for printing out.
4240         * @param faultId This is the identifier of the fault 
4241         * @param PDFDocument This is the PDF document to which the fault details are to be added 
4242         * @throws UnknownFaultSpecifiedException If the fault specified cannot be found. 
4243         */
4244        
4245        void createFaultReportAsPDF (final int faultId,final Document PDFDocument) throws UnknownFaultSpecifiedException {
4246            
4247            PdfPCell cell;
4248            
4249            // This is the font which will be used for the headers
4250    
4251            final Font headerFont = new Font(Font.HELVETICA, 10,Font.BOLD);
4252    
4253            // This is the fault which will be used for the body of the
4254            // text
4255    
4256            final Font bodyFont = new Font(Font.HELVETICA,8);
4257            
4258            // Create a 4 column table into which we will drop the
4259            // details of the fault
4260    
4261            PdfPTable table = new PdfPTable (new float [] {
4262                                              1.0f,
4263                                              1.0f,
4264                                              1.0f,
4265                                              1.0f,
4266                                          });
4267            
4268            // Get the details of the fault from the database
4269            
4270            final Fault faultDetails = getFaultDetails (faultId,"en");
4271            
4272            if (faultDetails == null) {
4273                throw new UnknownFaultSpecifiedException("The fault " + faultId + " does not exist in the database");
4274            }
4275            
4276            final Solution solution = getSolutionDetails(faultId);
4277            
4278            final Workaround workAround = getWorkaroundDetails(faultId);
4279            
4280            // Now get all of the comments associated with the fault
4281    
4282            final ArrayList<Comment> faultComments = getCommentDetails(faultId);
4283            
4284            // Now we need to prepare the PDF document for display
4285            
4286            table.setWidthPercentage(90.0f);
4287            
4288            // Now the title of the report
4289    
4290            cell = new PdfPCell(new Paragraph("Defect Report " + faultId,headerFont));
4291            
4292            cell.setColspan(4);
4293            cell.setHorizontalAlignment (Element.ALIGN_CENTER);
4294            cell.setPadding (10.0f);
4295            table.addCell (cell);
4296            
4297            // Now add the text of the title 
4298    
4299            cell = new PdfPCell(new Paragraph(removeHTMLMarkUp(faultDetails.title),headerFont));
4300            cell.setColspan(4);
4301            cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4302            cell.setPadding (10.0f);
4303            table.addCell (cell);
4304    
4305            // Now for the fault description banner 
4306    
4307            cell = new PdfPCell(new Paragraph("Description",headerFont));
4308            cell.setColspan(4);
4309            cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4310            cell.setPadding (10.0f);
4311            table.addCell (cell);
4312            
4313            // Now the description of the fault 
4314    
4315            cell = new PdfPCell(new Paragraph(removeHTMLMarkUp(faultDetails.description),bodyFont));
4316                
4317            cell.setColspan(4);
4318            cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4319            cell.setPadding (10.0f);
4320            table.addCell (cell);
4321            
4322            // Now for the first row of the table 
4323            
4324            cell = new PdfPCell(new Paragraph("Time Occurred",bodyFont));
4325            cell.setPadding(5.0f);
4326            table.addCell (cell);
4327    
4328            cell = new PdfPCell(new Paragraph(faultDetails.timeOccured,bodyFont));
4329            cell.setPadding(5.0f);
4330            table.addCell (cell);
4331    
4332    
4333            cell = new PdfPCell(new Paragraph("Date Occurred",bodyFont));
4334            cell.setPadding(5.0f);
4335            table.addCell (cell);
4336    
4337            cell = new PdfPCell(new Paragraph(faultDetails.dateOccured,bodyFont));
4338            cell.setPadding(5.0f);
4339            table.addCell (cell);
4340    
4341            cell = new PdfPCell(new Paragraph("Entered By",bodyFont));
4342            cell.setPadding(5.0f);
4343            table.addCell (cell);
4344    
4345            cell = new PdfPCell(new Paragraph(faultDetails.enteredByName + " " + faultDetails.enteredBySurname,bodyFont));
4346            cell.setPadding(5.0f);
4347            table.addCell (cell);
4348    
4349            cell = new PdfPCell(new Paragraph("Assigned to",bodyFont));
4350            cell.setPadding(5.0f);
4351            table.addCell (cell);
4352            
4353            // Make sure that the name of the assigned person is correctly
4354            // formatted if the name of the person is unknown.
4355    
4356            String assignedName;
4357            
4358            if (faultDetails.assignedToSurname.equals("Unknown") || faultDetails.assignedToSurname.equals(FaultDatabaseConstants.GUEST)) {
4359                assignedName = faultDetails.assignedToSurname;
4360            } else {
4361                assignedName = faultDetails.assignedToName + " " + faultDetails.assignedToSurname;
4362            }
4363            
4364            cell = new PdfPCell(new Paragraph(assignedName,bodyFont));
4365            cell.setPadding(5.0f);
4366            table.addCell (cell);
4367    
4368            cell = new PdfPCell(new Paragraph("Severity",bodyFont));
4369            cell.setPadding(5.0f);
4370            table.addCell (cell);
4371    
4372            cell = new PdfPCell(new Paragraph(faultDetails.severity,bodyFont));
4373            cell.setPadding(5.0f);
4374            table.addCell (cell);
4375    
4376            cell = new PdfPCell(new Paragraph("Site",bodyFont));
4377            cell.setPadding(5.0f);
4378            table.addCell (cell);
4379    
4380            cell = new PdfPCell(new Paragraph(faultDetails.site,bodyFont));
4381            cell.setPadding(5.0f);
4382            table.addCell (cell);
4383    
4384            cell = new PdfPCell(new Paragraph("Instrument",bodyFont));
4385            cell.setPadding(5.0f);
4386            table.addCell (cell);
4387    
4388            cell = new PdfPCell(new Paragraph(faultDetails.instrument,bodyFont));
4389            cell.setPadding(5.0f);
4390            table.addCell (cell);
4391    
4392            cell = new PdfPCell(new Paragraph("State",bodyFont));
4393            cell.setPadding(5.0f);
4394            table.addCell (cell);
4395    
4396            cell = new PdfPCell(new Paragraph(faultDetails.state,bodyFont));
4397            cell.setPadding(5.0f);
4398            table.addCell (cell);
4399            
4400            cell = new PdfPCell(new Paragraph("Type",bodyFont));
4401            cell.setPadding(5.0f);
4402            table.addCell (cell);
4403    
4404            cell = new PdfPCell(new Paragraph(faultDetails.faultType,bodyFont));
4405            cell.setPadding(5.0f);
4406            table.addCell (cell);
4407    
4408            cell = new PdfPCell(new Paragraph("Time Entered",bodyFont));
4409            cell.setPadding(5.0f);
4410            table.addCell (cell);
4411    
4412            cell = new PdfPCell(new Paragraph(faultDetails.timeEntered,bodyFont));
4413            cell.setPadding(5.0f);
4414            table.addCell (cell);
4415    
4416            cell = new PdfPCell(new Paragraph("Date Entered",bodyFont));
4417            cell.setPadding(5.0f);
4418            table.addCell (cell);
4419    
4420            cell = new PdfPCell(new Paragraph(faultDetails.dateEntered,bodyFont));
4421            cell.setPadding(5.0f);
4422            table.addCell (cell);
4423    
4424            cell = new PdfPCell(new Paragraph("Time Lost",bodyFont));
4425            cell.setPadding(5.0f);
4426            table.addCell (cell);
4427    
4428            cell = new PdfPCell(new Paragraph(faultDetails.timeLost,bodyFont));
4429            cell.setPadding(5.0f);
4430            table.addCell (cell);
4431    
4432            cell = new PdfPCell(new Paragraph("Observer",bodyFont));
4433            cell.setPadding(5.0f);
4434            table.addCell (cell);
4435    
4436            String tmp = faultDetails.observer;
4437            
4438            if (faultDetails.observer.equals("")) {
4439                tmp = "Unknown";
4440            }
4441            
4442            cell = new PdfPCell(new Paragraph(tmp,bodyFont));
4443            cell.setPadding(5.0f);
4444            table.addCell (cell);
4445    
4446            cell = new PdfPCell(new Paragraph("Time Spent",bodyFont));
4447            cell.setPadding(5.0f);
4448            table.addCell (cell);
4449    
4450            cell = new PdfPCell(new Paragraph(faultDetails.totalTimeSpentOnFault,bodyFont));
4451            cell.setPadding(5.0f);
4452            table.addCell (cell);
4453            
4454            // Add the table to the document 
4455    
4456            try {
4457                PDFDocument.add(table);
4458            } catch (DocumentException e) {
4459                logger.error("Unable to add the fault table to the document " + e.getMessage());
4460            }
4461            
4462            // Now for all of the comments associated with the fault we
4463            // should print them out
4464    
4465            for (int i=0; i < faultComments.size(); i++) { 
4466    
4467                final Comment comment = faultComments.get(i);
4468    
4469                logger.debug("Adding comment to report for fault");
4470    
4471                // Now add the details of the comment to the report 
4472                
4473                table = new PdfPTable (new float [] {
4474                                           1.0f,
4475                                       });
4476                
4477                table.setWidthPercentage(90.0f);
4478                
4479                // Now the title banner of the comment 
4480                
4481                cell = new PdfPCell(new Paragraph("Comment",headerFont));
4482                cell.setPadding (10.0f);
4483                table.addCell(cell);
4484                
4485                cell = new PdfPCell(new Paragraph("By " + comment.enteredByName + " " + comment.enteredBySurname + " on the " + comment.dateEntered + " who spent " + comment.timeSpent + " (HH:MM)",bodyFont));
4486                cell.setPadding (5.0f);
4487                        
4488                cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4489                table.addCell(cell);
4490    
4491                // Add the description of the comment to the document 
4492    
4493                cell = new PdfPCell(new Paragraph(removeHTMLMarkUp(comment.description),bodyFont));
4494                
4495                cell.setPadding (10.0f);
4496                
4497                table.addCell (cell);
4498    
4499                try {
4500                    PDFDocument.add(table);
4501                } catch (DocumentException e) {
4502                    logger.error("Unable to add the comment to the document " + e.getMessage());
4503                }
4504            }
4505            
4506            // Now report any workaround which has been entered 
4507            
4508            if (workAround != null) {
4509                
4510                logger.debug("Adding workaround to report for fault");
4511    
4512                // Now add the details of the workaround to the report 
4513                
4514                table = new PdfPTable (new float [] {
4515                                           1.0f,
4516                                       });
4517                
4518                table.setWidthPercentage(90.0f);
4519                
4520                // Now the title banner of the workaround 
4521                
4522                cell = new PdfPCell(new Paragraph("Workaround",headerFont));
4523                cell.setPadding (10.0f);
4524                table.addCell(cell);
4525                
4526                cell = new PdfPCell(new Paragraph("By " + workAround.enteredByName + " " + workAround.enteredBySurname + " on the " + workAround.dateEntered + " who spent " + workAround.timeSpent + " (HH:MM)",bodyFont));
4527                cell.setPadding (5.0f);
4528                        
4529                cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4530                table.addCell(cell);
4531                
4532                cell = new PdfPCell(new Paragraph(removeHTMLMarkUp(workAround.description),bodyFont));
4533                cell.setPadding (10.0f);
4534                
4535                table.addCell (cell);
4536    
4537                try {
4538                    PDFDocument.add(table);
4539                } catch (DocumentException e) {
4540                    logger.error("Unable to add the workaround to the document " + e.getMessage());
4541                }
4542                
4543            }
4544            
4545            // Now report any solution which has been entered 
4546            
4547            if (solution != null) {
4548                
4549                // Now add the details of the solution to the report 
4550                
4551                table = new PdfPTable (new float [] {
4552                                           1.0f,
4553                                       });
4554                
4555                table.setWidthPercentage(90.0f);
4556                
4557                // Now the title banner of the solution 
4558                
4559                cell = new PdfPCell(new Paragraph("Solution",headerFont));
4560                cell.setPadding (10.0f);
4561                table.addCell(cell);
4562                
4563                cell = new PdfPCell(new Paragraph("By " + solution.enteredByName + " " + solution.enteredBySurname + " on the " + solution.dateEntered + " who spent " + solution.timeSpent + " (HH:MM)",bodyFont));
4564                cell.setPadding (5.0f);
4565                        
4566                cell.setHorizontalAlignment (Element.ALIGN_LEFT);
4567                table.addCell(cell);
4568                
4569                cell = new PdfPCell(new Paragraph(removeHTMLMarkUp(solution.description),bodyFont));
4570                cell.setPadding (10.0f);
4571                
4572                table.addCell (cell);
4573    
4574                try {
4575                    PDFDocument.add(table);
4576                } catch (DocumentException e) {
4577                    logger.error("Unable to add the solution to the document " + e.getMessage());
4578                }
4579            }
4580        }
4581    
4582        /**
4583         * Return a list of all of the printers which are currently
4584         * accesible by this machine
4585         * @return ArrayList<String> A list of the printers which can print
4586         * postscript from this machine
4587         * @gwt.typeArgs <java.lang.String>  
4588         */
4589        
4590        public ArrayList<String> getPostscriptPrinters () {
4591    
4592            ArrayList<String> printerList = new ArrayList<String>();
4593    
4594            for (PrintService i : PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.POSTSCRIPT,null)) {
4595                printerList.add(i.getName());
4596            }
4597            
4598            logger.debug("Found " + printerList.size() + " printers on the host computer");
4599    
4600            return printerList;
4601        }
4602        
4603        /**
4604         * Used to remove all HTML up characters in a string so that the
4605         * text can be printed in a PDF document.
4606         * @param inputString The string to be converted. 
4607         * @return The converted string without the markup characters. 
4608         */
4609    
4610        private String removeHTMLMarkUp (final String inputString) {
4611            
4612            String processedString = inputString;
4613    
4614            // By default if the user has used the tags H1-6 through the
4615            // use of the editor widget then we need to augment the final
4616            // tag with a new line as that is not added. 
4617    
4618            processedString = processedString.replaceAll("</h[1-6]+>","\n");
4619            processedString = processedString.replaceAll("<p>","\n\n");
4620            processedString = processedString.replaceAll("&nbsp;"," ");
4621            processedString = processedString.replaceAll("&gt;",">");
4622            processedString = processedString.replaceAll("&lt;",">");
4623            processedString = processedString.replaceAll("&amp;","&");
4624            processedString = processedString.replaceAll("&quot;","'");
4625    
4626    
4627            // Try and preserve the structure of lists
4628            
4629            processedString = processedString.replaceAll("<br></li>","</li>");            
4630            processedString = processedString.replaceAll("<li>","\to ");
4631            processedString = processedString.replaceAll("</li>","\n");
4632            processedString = processedString.replaceAll("<ul>","\n");
4633            processedString = processedString.replaceAll("<ol>","\n");
4634    
4635            processedString = processedString.replaceAll("<br.*?>","\n");
4636            processedString = processedString.replaceAll("\n{3,}?","\n\n");
4637    
4638            return processedString.replaceAll("<.*?>","");
4639        }
4640    
4641        /**
4642         * Will be used to execute an advanced search across the database
4643         * using the input data which has been provided. 
4644         * <p>
4645         * @param SearchParameters These are the search parameters which
4646         * will be used to search across the database.
4647         * @param locale This is the locale which we are operating in
4648         * @param StartDate This is the start date from when to look
4649         * from. This could be left blank. 
4650         * @param EndDate This is the end date from when to stop looking
4651         * for faults.      
4652         * @throws UnableToPerformSearchException If we were unable to
4653         * perform the search for whatever reason.
4654         * @gwt.typeArgs <GWTApplication.client.Fault>
4655         */
4656        
4657        public ArrayList<Fault> performAdvancedSearch (final HashMap SearchParameters,
4658                                                       String StartDate, 
4659                                                       String EndDate,                                                 
4660                                                       final String locale) throws UnableToPerformSearchException {
4661    
4662    
4663            /**
4664             * A entity class which is used to hold the data associated
4665             * with the fields in the advanced search 
4666             */
4667            
4668            class AdvancedSearchParameter {
4669    
4670                /**
4671                 * This is name of the index into the hashmap which is
4672                 * passed down by the client for this parameter
4673                 */
4674                
4675                 String index;
4676                
4677                /**
4678                 * This is the data type of the attribute in the table. 
4679                 */
4680                
4681                AdvancedDataType dataType;
4682                
4683                /**
4684                 * This is the name of the attribute in the database table
4685                 * which we need to search over
4686                 */
4687    
4688                String tableAttribute;
4689    
4690                /**
4691                 * Constructor 
4692                 */
4693    
4694                AdvancedSearchParameter(final String index, final AdvancedDataType dataType, final String tableAttribute) {
4695                    this.index = index;
4696                    this.dataType = dataType;
4697                    this.tableAttribute = tableAttribute;
4698                }
4699            }
4700            
4701            // This data structure will be used to help construct the
4702            // advanced search query
4703    
4704            final AdvancedSearchParameter advancedSearchParameters[] = {
4705                new AdvancedSearchParameter(SearchForm.OBSERVER,AdvancedDataType.STRING,"OBSERVER"),
4706                new AdvancedSearchParameter(SearchForm.SEVERITY,AdvancedDataType.STRING,"SEVERITY"),
4707                new AdvancedSearchParameter(SearchForm.INSTRUMENT,AdvancedDataType.STRING,"INSTRUMENT"),
4708                new AdvancedSearchParameter(SearchForm.SITE,AdvancedDataType.STRING,"SITE_ID"),
4709                new AdvancedSearchParameter(SearchForm.SYSTEM,AdvancedDataType.STRING,"SYSTEM_ID"),
4710                new AdvancedSearchParameter(SearchForm.ORIGINATOR,AdvancedDataType.STRING,"ENTEREDBY_EMAIL"),
4711                new AdvancedSearchParameter(SearchForm.TO,AdvancedDataType.STRING,"TO_EMAIL"),
4712                new AdvancedSearchParameter(SearchForm.DUTYENGINEER,AdvancedDataType.STRING,"DUTYTECHNICIAN_EMAIL"),
4713                new AdvancedSearchParameter(SearchForm.SA,AdvancedDataType.STRING,"SA_EMAIL"),
4714                new AdvancedSearchParameter(SearchForm.ASSIGNEDTO,AdvancedDataType.STRING,"ASSIGNEDTO_EMAIL"),
4715                new AdvancedSearchParameter(SearchForm.FAULTTYPE,AdvancedDataType.STRING,"FAULTTYPE_ID"),
4716                new AdvancedSearchParameter(SearchForm.STATUSTYPE,AdvancedDataType.STRING,"STATE_ID"),
4717            };
4718            
4719            // The results of the search 
4720            
4721            final ArrayList<Fault> matchingFaults = new ArrayList<Fault>();
4722            
4723            // The free format text fields which have been specified by
4724            // the user in the keywords box in the search form 
4725    
4726            String searchTerm = (String)SearchParameters.get(SearchForm.KEYWORDS);
4727    
4728            String[] searchItems = null;
4729            
4730            // This will be used for joining all of the various search terms
4731    
4732            String searchTermsBooleanOperator,keywordsBooleanOperator;
4733    
4734            // This is the boolean operator which will be used for
4735            // conjugating the keyword search terms
4736    
4737            if (((String)SearchParameters.get(SearchForm.USEORKEYWORDS)).equals(SearchForm.CHECKED)) {
4738                keywordsBooleanOperator = " or ";
4739            } else {
4740                keywordsBooleanOperator = " and ";              
4741            }
4742            
4743            // Identify if the user has indicated that he would like the
4744            // search terms conjugated using OR instead of AND
4745                    
4746            if (((String)SearchParameters.get(SearchForm.USEOR)).equals(SearchForm.CHECKED)) {
4747                searchTermsBooleanOperator = " or ";
4748            } else {
4749                searchTermsBooleanOperator = " and ";                   
4750            }
4751    
4752            // Convert all of the * characters in the search term to %
4753            // symbols to be SQL compatible 
4754            
4755            if (searchTerm != null) {
4756    
4757                searchTerm = searchTerm.trim();
4758    
4759                searchTerm = searchTerm.replace('*','%');
4760                searchTerm = searchTerm.replace("_","\\_");
4761                searchTerm = searchTerm.replace("'","\\'");
4762    
4763                // Now break up the search string based on spaces and spaces
4764                // between quoted text. 
4765                
4766                searchItems = searchTerm.split("[ ]|\"[ ]?\"",0);
4767            }
4768            
4769            // Now create a SQL statement which can be used to go through
4770            // the faults 
4771            
4772            String view;
4773            
4774            // Now depending on the locale we search different views 
4775            
4776            if (locale.equalsIgnoreCase("en")) {
4777                view = "faultview_en";
4778            } else {
4779                view = "faultview_sp";
4780            }
4781            
4782            // For each text field in the view we now create a search
4783            // across the fields. 
4784    
4785            final StringBuilder sqlClause = new StringBuilder("select * from " + view + " where ");
4786            
4787            boolean firstClauseInWhereStatement = true;
4788            
4789            // If the user specified anything in the keywords field then
4790            // we should add these search terms into the SQL statement
4791    
4792            if (searchTerm != null) {
4793    
4794                for (int j=0; j < searchItems.length; j++) { 
4795    
4796                    if (firstClauseInWhereStatement) {
4797    
4798                        sqlClause.append("(");
4799                        firstClauseInWhereStatement = false;
4800                        
4801                    } else {
4802                        
4803                        // In the description and title field, if we
4804                        // have more than one search term, by default
4805                        // we perform an AND on the search terms e.g
4806                        // if the user specifies "petals closing",
4807                        // then the search engine will match
4808                        // descriptions which have the words petals
4809                        // and closing in them. However, the user may
4810                        // modify this behaviour by clicking upon the
4811                        // checkbox labelled Use Or in Keywords ......
4812                            
4813                        sqlClause.append(keywordsBooleanOperator);
4814                    }
4815                                        
4816                    sqlClause.append("lower(CONJUGATEDTEXTFIELD) LIKE '%" + searchItems[j].toLowerCase() + "%'");
4817                    
4818                    // Establish if the user has entered an integer value for the
4819                    // search term and if he has then we have to try and match
4820                    // that value against a fault identifier
4821                    
4822                    try {
4823                        
4824                        Integer.valueOf(searchTerm);
4825                            
4826                        // We managed to parse the integer so now add an SQL term
4827                        
4828                        sqlClause.append(" or id = " + searchTerm);
4829                        
4830                    } catch (NumberFormatException e) {
4831                        // Do nothing if the search term is not an integer. 
4832                    }
4833                    
4834                    firstClauseInWhereStatement = false;
4835                }   
4836            }
4837                
4838            // Now we need to go through the rest of the terms which the
4839            // user may have entered into the form and then add them into
4840            // the search query
4841            
4842            if (!firstClauseInWhereStatement) {
4843                sqlClause.append(")");
4844            }           
4845            
4846            // Now we need to add in all of the additional clauses that
4847            // the user may have specified
4848    
4849            // Add in the DATEOCCURED clause if the user specified it. Note
4850            // that the user can enter a > or < operator which should be
4851            // respected. 
4852    
4853            String dateOccured = (String)SearchParameters.get(SearchForm.DATEOCCURED);
4854    
4855            // If this is the first clause after the keywords, we need to
4856            // AND the result of the keyword search clause with the result
4857            // of the search across all of the specific fields. 
4858    
4859            final StringBuilder specificTermSubClause = new StringBuilder();
4860            
4861            boolean specificSearchTermsFound = false;
4862            
4863            // Whether this is the first term in the subclause which
4864            // contains the list terms which have been additionally
4865            // selected by the user in the advanced search panel 
4866    
4867            boolean firstSpecificTermInSubClause = true;
4868            
4869            if (dateOccured != null) {
4870    
4871                final String operator = firstSpecificTermInSubClause?"":searchTermsBooleanOperator;
4872                        
4873                dateOccured = dateOccured.replaceAll(" ","");
4874                dateOccured = dateOccured.replaceAll(">",">'");
4875                dateOccured = dateOccured.replaceAll("<","<'");
4876                
4877                if (dateOccured.contains(">") || dateOccured.contains("<")) {
4878                    specificTermSubClause.append(operator + "TIMEOCCURED" + dateOccured + "'");
4879                } else {
4880                    specificTermSubClause.append(operator + "TIMEOCCURED = '" + dateOccured + "'");                         
4881                }
4882                
4883                firstSpecificTermInSubClause = false;
4884                specificSearchTermsFound     = true;
4885            }
4886    
4887            // Add in the PRIORITY if the user specified it. Note
4888            // that the user can enter a > or < operator which should be
4889            // respected. 
4890    
4891            String priority = (String)SearchParameters.get(SearchForm.PRIORITY);
4892            
4893            if (priority != null) {
4894    
4895                final String operator = firstSpecificTermInSubClause?"":searchTermsBooleanOperator;
4896    
4897                if (priority.contains(">") || priority.contains("<")) {
4898                    specificTermSubClause.append(operator + "PRIORITY" + priority);
4899                } else {
4900                    specificTermSubClause.append(operator + "PRIORITY = " + priority);                              
4901                }
4902                
4903                firstSpecificTermInSubClause = false;
4904                specificSearchTermsFound = true;                
4905            }
4906    
4907            // Add in the TIMELOST clause if the user specified it. Note
4908            // that the user can enter a > or < operator which should be
4909            // respected.
4910    
4911            String timeLost = (String)SearchParameters.get(SearchForm.TIMELOST);
4912            
4913            if (timeLost != null) {
4914    
4915                final String operator = firstSpecificTermInSubClause?"":searchTermsBooleanOperator;
4916    
4917                timeLost = timeLost.replaceAll(" ","");
4918                timeLost = timeLost.replaceAll(">",">'");
4919                timeLost = timeLost.replaceAll("<","<'");         
4920                
4921                if (timeLost.contains(">") || timeLost.contains("<")) {
4922                    specificTermSubClause.append(operator + " TIMELOST_INTERVAL " + timeLost + "'");
4923                } else {
4924                    specificTermSubClause.append(operator + " TIMELOST_INTERVAL = '" + timeLost + "'");                             
4925                }
4926                
4927                firstSpecificTermInSubClause = false;                   
4928                specificSearchTermsFound = true;                
4929            }
4930    
4931            // Add in the defect number clause if the user specified it. 
4932    
4933            final String defectNumber = (String)SearchParameters.get(SearchForm.DEFECTNUMBER);
4934            
4935            if (defectNumber != null) {
4936    
4937                final String operator = firstSpecificTermInSubClause?"":searchTermsBooleanOperator;
4938    
4939                if (defectNumber.contains(">") || defectNumber.contains("<")) {
4940                    specificTermSubClause.append(operator + "ID" + defectNumber);
4941                } else {
4942                    specificTermSubClause.append(operator + "ID = " + defectNumber + " ");                          
4943                }
4944                
4945                firstSpecificTermInSubClause = false;                                   
4946                specificSearchTermsFound = true;
4947                        
4948            }
4949    
4950            // Now add in any of the advanced fields into the search
4951            // query 
4952    
4953            for (int i=0; i < advancedSearchParameters.length ;i++) { 
4954                
4955                String operator = firstSpecificTermInSubClause?"":searchTermsBooleanOperator;           
4956    
4957                final AdvancedSearchParameter searchParameter  = advancedSearchParameters[i];
4958    
4959                String searchKey;
4960                
4961                searchKey = (String)SearchParameters.get(searchParameter.index);
4962                
4963                if (searchKey != null) {
4964    
4965                    searchKey = searchKey.toLowerCase();
4966    
4967                    switch (searchParameter.dataType) { 
4968                        
4969                    case STRING : 
4970                        specificTermSubClause.append(operator + "lower(" + searchParameter.tableAttribute + ") ='" + searchKey + "'");
4971                        firstSpecificTermInSubClause = false;
4972                        break;
4973                        
4974                    case NONSTRING : 
4975                        specificTermSubClause.append(operator + "lower(" + searchParameter.tableAttribute + ")=" + searchKey);
4976                        firstSpecificTermInSubClause = false;
4977                        break;
4978                    }
4979                    
4980                    specificSearchTermsFound = true;                            
4981                }               
4982            }
4983    
4984            // If we added a specific search term, then close the
4985            // bracketing
4986            
4987            if (specificSearchTermsFound) {
4988    
4989                if (!firstClauseInWhereStatement) {
4990                    sqlClause.append(" and (" + specificTermSubClause + ")");
4991                } else {
4992                    sqlClause.append(" (" + specificTermSubClause + ")");                           
4993                    firstClauseInWhereStatement = false;                            
4994                }
4995            }
4996            
4997            // If the user has specified a time window then we should add
4998            // that into the SQL as well 
4999    
5000            if (!EndDate.equals("") && !StartDate.equals("")) {
5001    
5002                // If the user has specified both dates we need to ensure
5003                // that the start date is before the end date and if not
5004                // swap them around.
5005                
5006                final String[] endDateInFields = EndDate.split("/");
5007                
5008                final String[] startDateInFields = StartDate.split("/");
5009                
5010                final GregorianCalendar startDate,endDate;
5011    
5012                try {
5013                    startDate = new GregorianCalendar(Integer.parseInt(startDateInFields[2]),Integer.parseInt(startDateInFields[1]),Integer.parseInt(startDateInFields[0]));
5014                    endDate   = new GregorianCalendar(Integer.parseInt(endDateInFields[2]),Integer.parseInt(endDateInFields[1]),Integer.parseInt(endDateInFields[0]));
5015                } catch(NumberFormatException e) {
5016                    throw new UnableToPerformSearchException("Problem formatting date fields");
5017                }
5018                
5019                // If the start date is after the end date then swap them around 
5020    
5021                if (startDate.compareTo(endDate) == 1) {
5022                    
5023                    String temp = StartDate;
5024                    
5025                    StartDate = EndDate;
5026                    EndDate   = temp;
5027                }
5028                
5029                // If there are additional clauses in the search then add
5030                // a boolean operator 
5031    
5032                if (!firstClauseInWhereStatement) {
5033                    sqlClause.append(" and ");
5034                }
5035                
5036                // Add a day onto the end date to make sure that the time
5037                // window is inclusive of the final day 
5038                
5039                sqlClause.append(" timeentered >= '" + StartDate + "' and timeentered <= cast(cast ('" + EndDate + "' as date) + cast ('1 day' as interval) as date) ");
5040    
5041            } else if (!EndDate.equals("")) {
5042                
5043                // If there are additional clauses in the search then add
5044                // a boolean operator 
5045    
5046                if (!firstClauseInWhereStatement) {
5047                    sqlClause.append(" and ");
5048                }
5049                
5050                // Add a day onto the end date to make sure that the time
5051                // window is inclusive of the final day 
5052                
5053                sqlClause.append(" timeentered <= cast(cast ('" + EndDate + "' as date) + cast ('1 day' as interval) as date) ");
5054                
5055            } else if (!StartDate.equals("")) {
5056    
5057                // If there are additional clauses in the search then add
5058                // a boolean operator 
5059    
5060                if (!firstClauseInWhereStatement) {
5061                    sqlClause.append(" and ");
5062                }
5063                
5064                // Add a day onto the end date to make sure that the time
5065                // window is inclusive of the final day 
5066                
5067                sqlClause.append(" timeentered >= '" + StartDate + "'");
5068            } 
5069            
5070            sqlClause.append(" order by id desc limit  " + MAXNUMBEROFRESULTS);
5071    
5072            logger.debug("Search string is : " + sqlClause);
5073            
5074            // Now get the fault data back from the database 
5075            
5076            Connection connection  = null;
5077            Statement  stmt = null;
5078            ResultSet  rs   = null;
5079                            
5080            // Now get a database connection from the connection pool if
5081            // we can
5082            
5083            if (dataSource == null) {
5084                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5085            }
5086                    
5087            try {
5088    
5089                connection = dataSource.getConnection();
5090                stmt       = connection.createStatement();
5091                rs         = stmt.executeQuery(sqlClause.toString());
5092                
5093                while (rs.next()) {
5094    
5095                    Fault faultData = packFault(rs,true);
5096                    matchingFaults.add(faultData);
5097                } 
5098    
5099                // Ensure that we clean up the database connection
5100                // correctly so that the connection is returned to the
5101                // database connection pool
5102    
5103                rs.close();
5104                connection.close();
5105                stmt.close();
5106                
5107                rs         = null;
5108                connection = null;
5109                stmt       = null;
5110    
5111            } catch (SQLException e) {
5112                e.printStackTrace();
5113                throw new UnableToPerformSearchException("Database error : " + e.getMessage());
5114            } finally {
5115               
5116                // Always make sure result sets and statements are closed,
5117                // and the connection is returned to the pool
5118    
5119                closeDatabaseConnections(connection,stmt,rs);           
5120    
5121            }
5122            
5123            return matchingFaults;
5124        }
5125    
5126        /**
5127         * Called to close a fault down by a user with administration
5128         * priviledges. 
5129         * @param faultId This is is the number of the fault which is
5130         * to be closed down.
5131         * @throws UnableToCloseFaultException Thrown when the method is
5132         * unable to close down the fault for whatever reason.
5133         */
5134    
5135        public void closeFault (final int faultId) throws UnableToCloseFaultException {
5136            
5137            // Now set the state of the fault in the database accordingly
5138            
5139            Connection connection  = null;
5140            Statement  stmt = null;
5141                            
5142            // Now get a database connection from the connection pool if
5143            // we can
5144            
5145            if (dataSource == null) {
5146                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5147            }
5148                    
5149            try {
5150    
5151                connection = dataSource.getConnection();
5152                stmt       = connection.createStatement();
5153    
5154                // If a user closes a fault down we move it to state
5155                // RELEASED. 
5156    
5157                stmt.execute("update fault set faultopen = 'false',state = 'RELEASED', lastmodified = 'NOW' where id = " + faultId);
5158    
5159                connection.close();
5160                stmt.close();
5161                
5162                connection = null;
5163                stmt       = null;
5164    
5165            } catch (SQLException e) {
5166                e.printStackTrace();
5167                throw new UnableToCloseFaultException ("Database error : " + e.getMessage());
5168            } finally {
5169               
5170                // Always make sure result sets and statements are closed,
5171                // and the connection is returned to the pool
5172    
5173                closeDatabaseConnections(connection,stmt,null);         
5174            }
5175            
5176            // Inform all email listeners that the fault has now been closed down 
5177    
5178            informEmailListenersFaultHasChanged("This fault has now been closed down", faultId);
5179        }
5180        
5181        /**
5182         * Get a list of the faults from the database which have been
5183         * updated since the last time that this client checked the
5184         * database. We return to the caller an array of the full history
5185         * of the faults which have been changed.     
5186         * @param timeOfLastUpdateCheck This is the time that we last
5187         * checked to see if the faults we are interested in were updated
5188         * @param locale This is the locale which we are operating in 
5189         * @return FaultUpdateSnapshot The details of all of the faults
5190         * which have been updated since the time specified 
5191         */
5192        
5193        public FaultUpdateSnapshot getFaultsUpdated (Date timeOfLastUpdateCheck, final String locale) {
5194            
5195            final FaultUpdateSnapshot updatedFaults = new FaultUpdateSnapshot();
5196            
5197            String view;
5198    
5199            // This is the time from which we need to search from when
5200            // comparing against the lastmodified attribute of the fault
5201            // table.
5202    
5203            java.util.GregorianCalendar dateToSearchFrom = new java.util.GregorianCalendar();
5204    
5205            if (timeOfLastUpdateCheck == null) {
5206                
5207                Date currentTime = new Date();
5208                
5209                timeOfLastUpdateCheck = new Date(currentTime.getTime() - 20000L);
5210            } 
5211            
5212            dateToSearchFrom.setTime(timeOfLastUpdateCheck);
5213            
5214            // Now we need to convert the time which has been sent to us
5215            // from the client into a time which we can use to compare
5216            // against the lastmodified timestamp in the fault table
5217            
5218            final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS");
5219            
5220            // Execute the SQL statement and return the details to the caller 
5221    
5222            Connection connection  = null;
5223            Statement  stmt = null;
5224            ResultSet  rs   = null;
5225    
5226            // Now get a database connection from the connection pool if
5227            // we can
5228            
5229            if (dataSource == null) {
5230                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5231            }
5232    
5233            try {
5234                
5235                connection = dataSource.getConnection();
5236                stmt       = connection.createStatement();
5237                
5238                rs         = stmt.executeQuery("select id from fault where lastmodified  > '" + dateFormatter.format(dateToSearchFrom.getTime()) + "'");
5239                
5240                //logger.debug("select id from fault where lastmodified  > '" + dateFormatter.format(dateToSearchFrom.getTime()) + "'");
5241                
5242                while (rs.next()) {
5243                    
5244                    // Add the details of the update fault to the list of
5245                    // faults which will be returned. 
5246    
5247                    updatedFaults.faultsUpdated.add(getFullFaultHistory(rs.getInt("id"),locale));
5248                }
5249    
5250                // Ensure that we clean up the database connection
5251                // correctly so that the connection is returned to the
5252                // database connection pool
5253    
5254                rs.close();
5255                connection.close();
5256                stmt.close();
5257                
5258                rs         = null;
5259                connection = null;
5260                stmt       = null;
5261    
5262            } catch (SQLException e) {
5263                e.printStackTrace();
5264            } finally {
5265    
5266                // Always make sure result sets and statements are closed,
5267                // and the connection is returned to the pool
5268    
5269                closeDatabaseConnections(connection,stmt,rs);           
5270            }
5271    
5272            //logger.debug("There are " + updatedFaults.faultsUpdated.size() + " faults which have been changed");
5273            
5274            return updatedFaults;
5275        }
5276    
5277        
5278        /**
5279         * Used by the client to establish if the fault actually exists in the database 
5280         * @param faultId The defect number of the fault to be checked
5281         * @param isEditable Check that the fault is editable
5282         * @return boolean True if the fault exists
5283        */
5284        
5285        public Boolean faultExists(final int faultId,final boolean isEditable) {
5286    
5287            Connection connection  = null;
5288            Statement  stmt        = null;
5289            ResultSet  rs          = null;
5290    
5291            Boolean faultFound = false;
5292    
5293            // Now get a database connection from the connection pool if
5294            // we can
5295            
5296            if (dataSource == null) {
5297                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5298            }
5299                    
5300            try {
5301            
5302                final StringBuffer SQLStatement = new StringBuffer("select id from fault where id = " + faultId);
5303                
5304                // If the caller has requested to check that the fault can
5305                // be edited as well then add in a check into the SQL
5306                // statement 
5307    
5308                if (isEditable) {
5309                    SQLStatement.append(" and faultopen = true");
5310                }
5311                
5312                connection = dataSource.getConnection();
5313                stmt       = connection.createStatement();
5314                rs         = stmt.executeQuery(SQLStatement.toString());
5315                
5316                if (rs.next()) {
5317                    faultFound = true;
5318                }
5319    
5320                // Ensure that we clean up the database connection
5321                // correctly so that the connection is returned to the
5322                // database connection pool
5323    
5324                rs.close();
5325                connection.close();
5326                stmt.close();
5327                
5328                rs         = null;
5329                connection = null;
5330                stmt       = null;
5331    
5332            } catch (SQLException e) {
5333                e.printStackTrace();
5334            } finally {
5335                closeDatabaseConnections(connection,stmt,rs);           
5336            }
5337    
5338            return faultFound;
5339        }
5340        
5341        /**
5342         * Used for closing down database connections 
5343         * @param connection Connection to the database to be closed. 
5344         * @param stmt  Statement to be closed. 
5345         * @param rs Result set to be closed. 
5346         */
5347        
5348        public void closeDatabaseConnections (Connection connection,Statement stmt, ResultSet rs) {
5349            
5350            if (rs != null) {
5351                try { 
5352                    rs.close(); 
5353                } catch (SQLException e) { ; }
5354            }
5355            
5356            if (stmt != null) {
5357                try { 
5358                    stmt.close(); 
5359                } catch (SQLException e) { ; }
5360            }
5361            
5362            if (connection != null) {
5363                try { 
5364                    connection.close(); 
5365                } catch (SQLException e) { ; }
5366            }
5367        }
5368    
5369        /**
5370         * This will be used to return the full history of a fault. This
5371         * includes the fault itself, any workaround, any solution, any
5372         * comments and any links. 
5373         * @param defectNumber The fault number for the fault 
5374         * @param locale This is the locale to use 
5375         * @return FaultEntryDetails The full history of the fault 
5376         */
5377        
5378        public FaultEntryDetails getFullFaultHistory (final int defectNumber,final String locale) {
5379            
5380            final FaultEntryDetails faultEntryDetails = new FaultEntryDetails();
5381            
5382            faultEntryDetails.fault      = getFaultDetails(defectNumber,locale);
5383            faultEntryDetails.solution   = getSolutionDetails(defectNumber);
5384            faultEntryDetails.workAround = getWorkaroundDetails(defectNumber);
5385            faultEntryDetails.comments   = getCommentDetails(defectNumber);
5386            faultEntryDetails.links      = getLinkedFaults(defectNumber);
5387            
5388            return faultEntryDetails;
5389        }    
5390        
5391        /**
5392         * This is used to get a list of the faults which the user specified is subscribed to 
5393         * @param authenticationDetails The details of the person that has logged into the system. 
5394         * @param locale The locale which the user has selected 
5395         * @return ArrayList This is a list of faults which the user is subscribed to 
5396         */
5397        
5398        public ArrayList getSubscribedFaults(final AuthenticationDetails authenticationDetails,final String locale) {
5399            
5400            final ArrayList<Fault> searchResults = new ArrayList();
5401            
5402            Connection connection  = null;
5403            Statement  stmt        = null;
5404            ResultSet  rs          = null;
5405    
5406            logger.debug("Getting list of subscribed faults for " + authenticationDetails.UserEmailAddress);
5407                
5408            // Now get a database connection from the connection pool if
5409            // we can
5410            
5411            if (dataSource == null) {
5412                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5413            }
5414                    
5415            try {
5416            
5417                final StringBuffer SQLStatement = new StringBuffer("select * from fault where faultopen = 'true' and id in (select id from faultlistener where email = '" + authenticationDetails.UserEmailAddress + "') order by id desc");
5418                
5419                connection = dataSource.getConnection();
5420                stmt       = connection.createStatement();
5421                rs         = stmt.executeQuery(SQLStatement.toString());
5422                
5423                int count = 0;
5424                
5425                while (rs.next()) {
5426                    Fault faultData = packFault(rs,false);
5427                    searchResults.add(faultData);           
5428                    count++;
5429                }
5430    
5431                logger.info("Found " + count + " faults which have been subscribed to by user " + authenticationDetails.UserEmailAddress);
5432    
5433            } catch (SQLException e) {
5434                e.printStackTrace();
5435            } finally {
5436                closeDatabaseConnections(connection,stmt,rs);           
5437            }
5438    
5439            return searchResults;
5440        }     
5441    
5442        /**
5443         * Will update the list of faults which a user is subscribed to.
5444         * @param authenticationDetails Details of the user 
5445         * @param newSubscriptionStatuses This is a hashmap of the subscription statues of faults 
5446         */
5447        
5448        public void updateFaultSubscriptionList (final AuthenticationDetails authenticationDetails, final HashMap newSubscriptionStatuses) {
5449    
5450            if (authenticationDetails == null || newSubscriptionStatuses == null) {
5451                logger.error("Input parameters not specified correctly");
5452                return;
5453            }
5454            
5455            // Get the email address of the person 
5456            
5457            final String userEmailAddress = authenticationDetails.UserEmailAddress;
5458    
5459            Iterator iterator = newSubscriptionStatuses.keySet().iterator();
5460            
5461            Connection connection  = null;
5462            PreparedStatement preparedStatement = null;
5463            
5464            // Now update the database table which is holding the
5465            // status of the updates.
5466            
5467            
5468            if (dataSource == null) {
5469                dataSource = (DataSource) getServletContext().getAttribute("DBCPool");
5470            }
5471                                
5472            try {       
5473    
5474                connection = dataSource.getConnection();
5475                    
5476                preparedStatement = connection.prepareStatement("delete from faultlistener where email = '" + userEmailAddress  + "' and id = ?");
5477    
5478                while (iterator.hasNext()) {
5479                    
5480                    final Integer defectNo = (Integer)iterator.next();
5481                    
5482                    final Boolean listenToFault = (Boolean) newSubscriptionStatuses.get(defectNo);
5483                    
5484                    // If the user does not want to listen to a fault then
5485                    // delete the subscription
5486                    
5487                    if (!listenToFault) {
5488                        preparedStatement.setInt(1,defectNo);
5489                        preparedStatement.executeUpdate();
5490                    }
5491                }
5492    
5493                preparedStatement.close();
5494    
5495            } catch (SQLException e) {
5496                logger.error("Unable to update the faultlistener table for " + userEmailAddress + " " + e.getMessage());
5497            } finally {
5498    
5499                try {
5500                    
5501                    if (preparedStatement != null) {
5502                        preparedStatement.close();
5503                    }
5504                    
5505                    if (connection != null) {
5506                        connection.close();
5507                    }
5508    
5509                } catch (SQLException e){
5510                    // Do nothing here
5511                }
5512            }
5513        }
5514    }