001    /**
002    The contents of this file are subject to the Mozilla Public License Version 1.1 
003    (the "License"); you may not use this file except in compliance with the License. 
004    You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
005    Software distributed under the License is distributed on an "AS IS" basis, 
006    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
007    specific language governing rights and limitations under the License. 
008    
009    The Original Code is "TestPanel.java".  Description: 
010    "A user interface for testing communications with an HL7 server." 
011    
012    The Initial Developer of the Original Code is University Health Network. Copyright (C) 
013    2001.  All Rights Reserved. 
014    
015    Contributor(s): ______________________________________. 
016    
017    Alternatively, the contents of this file may be used under the terms of the 
018    GNU General Public License (the "GPL"), in which case the provisions of the GPL are 
019    applicable instead of those above.  If you wish to allow use of your version of this 
020    file only under the terms of the GPL and not to allow others to use your version 
021    of this file under the MPL, indicate your decision by deleting  the provisions above 
022    and replace  them with the notice and other provisions required by the GPL License.  
023    If you do not delete the provisions above, a recipient may use your version of 
024    this file under either the MPL or the GPL. 
025    
026     */
027    package ca.uhn.hl7v2.app;
028    
029    import java.awt.BorderLayout;
030    import java.awt.Component;
031    import java.awt.FlowLayout;
032    import java.awt.event.ActionEvent;
033    import java.awt.event.ActionListener;
034    import java.awt.event.WindowAdapter;
035    import java.awt.event.WindowEvent;
036    import java.io.ByteArrayOutputStream;
037    import java.io.File;
038    import java.io.FileInputStream;
039    import java.io.IOException;
040    import java.io.InputStream;
041    import java.nio.charset.Charset;
042    import java.util.concurrent.ConcurrentLinkedQueue;
043    
044    import javax.swing.JButton;
045    import javax.swing.JCheckBox;
046    import javax.swing.JComboBox;
047    import javax.swing.JFileChooser;
048    import javax.swing.JFrame;
049    import javax.swing.JLabel;
050    import javax.swing.JList;
051    import javax.swing.JOptionPane;
052    import javax.swing.JPanel;
053    import javax.swing.JScrollPane;
054    import javax.swing.JSplitPane;
055    import javax.swing.JTextArea;
056    import javax.swing.SwingUtilities;
057    
058    import org.apache.log4j.BasicConfigurator;
059    import org.apache.log4j.Layout;
060    import org.apache.log4j.PatternLayout;
061    import org.apache.log4j.WriterAppender;
062    import org.apache.log4j.spi.LoggingEvent;
063    import org.slf4j.Logger;
064    import org.slf4j.LoggerFactory;
065    
066    import ca.uhn.hl7v2.HL7Exception;
067    import ca.uhn.hl7v2.llp.LLPException;
068    import ca.uhn.hl7v2.llp.LowerLayerProtocol;
069    import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
070    import ca.uhn.hl7v2.model.Message;
071    import ca.uhn.hl7v2.parser.EncodingNotSupportedException;
072    import ca.uhn.hl7v2.parser.GenericParser;
073    import ca.uhn.hl7v2.parser.Parser;
074    import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
075    import ca.uhn.hl7v2.view.TreePanel;
076    
077    /**
078     * <p>
079     * A user interface for testing communications with an HL7 server. The UI
080     * contains a window with 4 main panels. One can enter message text into the
081     * first panel, press a "parse" button so that the message is displayed in a
082     * tree in the second panel, press "send" to send the message to a remote server
083     * and display the response in a tree in the third panel, and press "encode" to
084     * write the inbound message as text in the fourth panel. To use, run from the
085     * command line with no arguments, like this:
086     * </p>
087     * <p>
088     * <code>java -classpath . ca.uhn.hl7v2.app.TestPanel</code>
089     * </p>
090     * <p>
091     * Exceptions generated during parsing or server communication are logged in the
092     * working directory and displayed in a dialog box.
093     * </p>
094     * 
095     * @author Bryan Tripp
096     * @author Christian Ohr
097     * @deprecated This class has been replaced by the standalone TestPanel application. 
098     *      The new testpanel is located in a different HAPI module called "hapi-testpanel"  
099     */
100    @SuppressWarnings("serial")
101    public class TestPanel extends JPanel implements ConnectionListener {
102    
103            private static final Logger log = LoggerFactory.getLogger(TestPanel.class);
104    
105            private static HL7Service service;
106    
107            private GenericParser parser;
108            private JTextArea outboundText;
109            private TreePanel outboundTree;
110            private JTextArea inboundText;
111            private TreePanel inboundTree;
112            private JSplitPane messages;
113            private JList connList = null;
114            private MessageTypeRouter router;
115            private JCheckBox xmlCheckBox;
116            private JFileChooser fc;
117            private JLabel connectionListening;
118            private JTextArea events;
119            private SwingLogAppender appender;
120            private boolean started;
121            private ConnectionHub connectionHub;
122    
123            /** Creates a new instance of TestPanel */
124            public TestPanel() throws HL7Exception {
125    
126                    this.parser = new GenericParser();
127                    // Relax all validation
128                    this.parser.setValidationContext(ValidationContextFactory.noValidation());
129                    connectionHub = ConnectionHub.getInstance();
130                    router = new MessageTypeRouter();
131                    initUI();
132                    BasicConfigurator.configure(appender);
133            }
134    
135            /**
136             * Creates and lays out UI components
137             */
138            private void initUI() {
139                    // main part of panel shows grid of 4 message areas: outbound on top;
140                    // inbound on bottom
141                    // and plain text on left, tree on right
142                    this.setLayout(new BorderLayout());
143                    outboundText = new JTextArea(10, 10);
144                    JScrollPane outTextScroll = new JScrollPane(outboundText);
145                    outboundTree = new TreePanel();
146                    JScrollPane outTreeScroll = new JScrollPane(outboundTree);
147                    inboundText = new JTextArea(10, 10);
148                    JScrollPane inTextScroll = new JScrollPane(inboundText);
149                    inboundTree = new TreePanel();
150                    JScrollPane inTreeScroll = new JScrollPane(inboundTree);
151    
152                    JSplitPane outbound = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
153                                    addTitle(outTextScroll, " Outbound Message Text "), addTitle(
154                                                    outTreeScroll, " Outbound Message Tree "));
155                    JSplitPane inbound = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true,
156                                    addTitle(inTextScroll, " Inbound Message Text "), addTitle(
157                                                    inTreeScroll, " Inbound Message Tree "));
158                    messages = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, outbound,
159                                    inbound);
160    
161                    events = new JTextArea(10, 10);
162                    JScrollPane eventsScroll = new JScrollPane(events);
163                    appender = new SwingLogAppender(events);
164                    JSplitPane messagesAndLog = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
165                                    true, messages, eventsScroll);
166                    this.add(messagesAndLog, BorderLayout.CENTER);
167    
168                    // controls arranged along top for now
169                    JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
170                    final JComboBox charsetChooser = new JComboBox(Charset
171                                    .availableCharsets().keySet().toArray(new String[0]));
172                    charsetChooser.setSelectedItem(Charset.defaultCharset().displayName());
173                    charsetChooser.setMaximumRowCount(15);
174                    JButton loadButton = new JButton(" Load ");
175                    JButton clearButton = new JButton(" Clear ");
176                    JButton parseButton = new JButton(" Parse ");
177                    JButton sendButton = new JButton(" Send ");
178                    JButton encodeButton = new JButton(" Encode Inbound ");
179                    JButton encodeOriginalButton = new JButton(" Encode Outbound ");
180                    // JButton xmlEncodeButton = new JButton(" Encode XML ");
181                    xmlCheckBox = new JCheckBox("Use XML", false);
182                    JButton connectButton = new JButton(" Connect ");
183                    JButton disconnectButton = new JButton(" Disconnect ");
184                    controlPanel.add(charsetChooser);
185                    controlPanel.add(loadButton);
186                    controlPanel.add(clearButton);
187                    controlPanel.add(parseButton);
188                    controlPanel.add(encodeButton);
189                    controlPanel.add(encodeOriginalButton);
190                    controlPanel.add(xmlCheckBox);
191                    controlPanel.add(connectButton);
192                    controlPanel.add(sendButton);
193                    controlPanel.add(disconnectButton);
194                    this.add(controlPanel, BorderLayout.NORTH);
195    
196                    // connection selector on right side
197                    connList = new JList();
198                    connList.setPrototypeCellValue("xxxxxxxxxxxxxxxxxxxxxxxxxxx");
199                    connList.setSelectionMode(0); // found through trial & error - don't
200                    // know where constants are defined
201                    JPanel connPanel = new JPanel(new BorderLayout());
202                    connPanel.add(new JScrollPane(connList), BorderLayout.CENTER);
203                    connectionListening = new JLabel(" Connections ");
204                    connPanel.add(connectionListening, BorderLayout.NORTH);
205                    this.add(connPanel, BorderLayout.EAST);
206    
207                    fc = new JFileChooser();
208    
209                    // set up event handlers for buttons
210                    loadButton.addActionListener(new ActionListener() {
211                            public void actionPerformed(ActionEvent ae) {
212                                    try {
213                                            int returnVal = fc.showOpenDialog(getThis());
214                                            if (returnVal == JFileChooser.APPROVE_OPTION) {
215                                                    File file = fc.getSelectedFile();
216                                                    // This is where a real application would open the file.
217                                                    String input = readFile(file, Charset
218                                                                    .forName((String) charsetChooser
219                                                                                    .getSelectedItem()));
220                                                    outboundText.setText(input);
221                                                    outboundText.setCaretPosition(0);
222                                            }
223    
224                                    } catch (Exception e) {
225                                            showException(e);
226                                    }
227                            }
228                    });
229    
230                    // set up event handlers for buttons
231                    clearButton.addActionListener(new ActionListener() {
232                            public void actionPerformed(ActionEvent ae) {
233                                    try {
234                                            outboundText.setText("");
235                                            inboundText.setText("");
236                                            inboundTree.setMessage(null);
237                                            outboundTree.setMessage(null);
238                                            events.setText("");
239    
240                                    } catch (Exception e) {
241                                            showException(e);
242                                    }
243                            }
244                    });
245    
246                    // set up event handlers for buttons
247                    parseButton.addActionListener(new ActionListener() {
248                            public void actionPerformed(ActionEvent ae) {
249                                    try {
250                                            parseOutbound();
251                                    } catch (Exception e) {
252                                            showException(e);
253                                    }
254                            }
255                    });
256    
257                    connectButton.addActionListener(new ActionListener() {
258                            public void actionPerformed(ActionEvent ae) {
259                                    try {
260                                            new ConnectionDialog(getThis());
261                                    } catch (Exception e) {
262                                            log.error(e.getMessage());
263                                            showException(e);
264                                    }
265                            }
266                    });
267    
268                    sendButton.addActionListener(new ActionListener() {
269                            public void actionPerformed(ActionEvent ae) {
270                                    try {
271                                            sendAndRecieve();
272                                    } catch (Exception e) {
273                                            showException(e);
274                                    }
275                            }
276                    });
277    
278                    encodeButton.addActionListener(new ActionListener() {
279                            public void actionPerformed(ActionEvent ae) {
280                                    try {
281                                            encodeInbound();
282                                    } catch (Exception e) {
283                                            showException(e);
284                                    }
285                            }
286                    });
287    
288                    encodeOriginalButton.addActionListener(new ActionListener() {
289                            public void actionPerformed(ActionEvent ae) {
290                                    try {
291                                            encodeOutbound();
292                                    } catch (Exception e) {
293                                            showException(e);
294                                    }
295                            }
296                    });
297    
298                    xmlCheckBox.addActionListener(new ActionListener() {
299                            public void actionPerformed(ActionEvent ae) {
300                                    if (xmlCheckBox.isSelected()) {
301                                            parser.setXMLParserAsPrimary();
302                                    } else {
303                                            parser.setPipeParserAsPrimary();
304                                    }
305                            }
306                    });
307    
308                    disconnectButton.addActionListener(new ActionListener() {
309                            public void actionPerformed(ActionEvent ae) {
310                                    try {
311                                            disconnect(getCurrentConnection());
312                                    } catch (Exception e) {
313                                            showException(e);
314                                    }
315                            }
316                    });
317    
318            }
319    
320            private String readFile(File file, Charset charset) throws IOException {
321                    byte[] buffer = new byte[4 * 1024];
322                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
323    
324                    InputStream in = null;
325                    try {
326                            in = new FileInputStream(file);
327                            int len = in.read(buffer);
328                            while (len >= 0) {
329                                    baos.write(buffer, 0, len);
330                                    len = in.read(buffer);
331                            }
332                            return new String(baos.toString(charset.name()));
333                    } catch (IOException e) {
334                            log.error("Failed reading file {}", file.getAbsolutePath(), e);
335                            throw e;
336                    } finally {
337                            if (in != null)
338                                    try {
339                                            in.close();
340                                    } catch (IOException e) {
341                                            // left empty
342                                    }
343                    }
344    
345            }
346    
347            /** Used in connect button handler ... got a better idea? */
348            private TestPanel getThis() {
349                    return this;
350            }
351    
352            /**
353             * Returns this TestPanel's underlying GenericParser. This method is needed
354             * by the HL7Service that the panel uses, so that it can share the parser
355             * and use whatever encoding it is using.
356             */
357            protected Parser getParser() {
358                    return this.parser;
359            }
360    
361            /**
362             * Adds a title to a component by putting it in another panel, adding the '
363             * title, and returning the new panel.
364             */
365            private static JPanel addTitle(Component toBeTitled, String title) {
366                    JPanel newPanel = new JPanel(new BorderLayout());
367                    newPanel.add(toBeTitled, BorderLayout.CENTER);
368                    newPanel.add(new JLabel(title), BorderLayout.NORTH);
369                    return newPanel;
370            }
371    
372            /**
373             * Displays an exception in a standard way. All exceptions for TestPanel are
374             * routed here. Currently this prints to std err, and displays an error
375             * dialog, but it could be changed.
376             */
377            public void showException(Exception e) {
378                    JOptionPane.showMessageDialog(this, e.getMessage(), e.getClass()
379                                    .getName(), JOptionPane.ERROR_MESSAGE);
380                    log.error("showException: ", e);
381            }
382    
383            /**
384             * Attempts to parse the contents of the outbound message text box into a
385             * message object and display in the outbound message tree.
386             */
387            public void parseOutbound() throws HL7Exception,
388                            EncodingNotSupportedException {
389                    // replace all the line feeds in the text area with carriage returns ...
390                    String messageString = this.outboundText.getText().replace('\n', '\r');
391                    Message out = parser.parse(messageString);
392                    this.outboundTree.setMessage(out);
393    
394                    if (messages.getDividerLocation() < 0)
395                            messages.setDividerLocation(0.5);
396                    this.validate();
397            }
398    
399            /**
400             * Returns the Connection that is currently selected. If the connection has
401             * been closed by the remote peer, the method attempts to reopen it.
402             * 
403             * @throws LLPException
404             * @throws IOException
405             */
406            private ConnectionData getCurrentConnection() throws Exception {
407                    ConnectionData ce = null;
408                    Object o = connList.getSelectedValue();
409                    if (o != null) {
410                            ce = (ConnectionData) o;
411                            if (!connectionHub.isOpen(ce)) {
412                                    disconnect(ce);
413                                    connect(ce);
414                            }
415                    }
416                    return ce;
417            }
418    
419            /**
420             * Sets up a connection to a remote server that uses the minimal lower layer
421             * protocol, and this TestPanel's GenericParser.
422             */
423            public void connect(String host, int port) throws Exception {
424                    connect(host, port, false);
425            }
426    
427            /**
428             * Sets up a connection to a remote server that uses the minimal lower layer
429             * protocol, and this TestPanel's GenericParser. // TODO ConnectionHub?
430             */
431            public void connect(String host, int port, boolean tls) throws Exception {
432                    Connection c = connectionHub.attach(host, port, parser,
433                                    MinLowerLayerProtocol.class, tls);
434                    c.getResponder().registerApplication(this.router);
435                    connList.setListData(connectionHub.allConnections().toArray(
436                                    new ConnectionData[0]));
437            }
438    
439            /**
440             * Sets up a connection to a remote server that uses the minimal lower layer
441             * protocol, and this TestPanel's GenericParser.
442             */
443            public void connect(String host, int inboundPort, int outboundPort)
444                            throws Exception {
445                    connect(host, inboundPort, outboundPort, false);
446            }
447    
448            /**
449             * Sets up a connection to a remote server that uses the minimal lower layer
450             * protocol, and this TestPanel's GenericParser.
451             */
452            public void connect(String host, int inboundPort, int outboundPort,
453                            boolean tls) throws Exception {
454                    Connection c = connectionHub.attach(host, outboundPort, inboundPort,
455                                    parser, MinLowerLayerProtocol.class, tls);
456                    c.getResponder().registerApplication(this.router);
457                    connList.setListData(connectionHub.allConnections().toArray(
458                                    new ConnectionData[0]));
459            }
460    
461            /**
462             * Sets up a connection to a remote server that uses the minimal lower layer
463             * protocol, and this TestPanel's GenericParser.
464             */
465            public void connect(ConnectionData cf) throws Exception {
466                    Connection c = connectionHub.attach(cf);
467                    c.getResponder().registerApplication(this.router);
468                    connList.setListData(connectionHub.allConnections().toArray(
469                                    new ConnectionData[0]));
470            }
471    
472            /** Notification that a new Connection has arrived at an HL7Service. */
473            public void connectionReceived(ca.uhn.hl7v2.app.Connection connection) {
474                    connection.getResponder().registerApplication(this.router);
475            }
476    
477            public void connectionDiscarded(Connection connection) {
478            }
479    
480            private void disconnect(ConnectionData c) {
481                    if (c != null) {
482                            connectionHub.detach(connectionHub.getKnownConnection(c));
483                            connList.setListData(connectionHub.allConnections().toArray(
484                                            new ConnectionData[0]));
485                    }
486            }
487    
488            /**
489             * Returns the MessageTypeRouter associated with this TestPanel. Every
490             * Connection that a TestPanel uses routes unsolicited messages through this
491             * MessageTypeRouter. Applications can be registered with the router using
492             * registerApplication().
493             */
494            public MessageTypeRouter getRouter() {
495                    return this.router;
496            }
497    
498            /**
499             * Sends the message that is currently displayed in the outbound tree to the
500             * remote system that is currently connected.
501             */
502            public void sendAndRecieve() throws Exception {
503                    Message outbound = this.outboundTree.getMessage();
504                    Message inbound;
505                    try {
506                            inbound = connectionHub.getKnownConnection(getCurrentConnection())
507                                            .getInitiator().sendAndReceive(outbound);
508                    } catch (NullPointerException e) {
509                            throw new IOException("Please select a Connection.");
510                    }
511                    this.inboundTree.setMessage(inbound);
512                    this.validate();
513            }
514    
515            /**
516             * Encodes the message that is currently displayed in the tree into a
517             * traditionally encoded message string and displays in the inbound message
518             * text box.
519             */
520            public void encodeInbound() throws HL7Exception {
521                    String inbound = this.parser.encode(this.inboundTree.getMessage());
522                    inbound = inbound.replace('\r', '\n');
523                    this.inboundText.setText(inbound);
524            }
525    
526            /**
527             * Encodes the message that is currently displayed in the outbound tree into
528             * a traditionally encoded message string and displays in a new window.
529             */
530            public void encodeOutbound() throws HL7Exception {
531                    String outbound = this.parser.encode(this.outboundTree.getMessage());
532                    outbound = outbound.replace('\r', '\n');
533                    TestPanel.openTextWindow("Outbound Message", outbound);
534            }
535    
536            /**
537             * Encodes the message that is currently displayed in the tree into an XML
538             * encoded message string and displays in the inbound message text box.
539             */
540            /*
541             * public void xmlEncodeInbound() throws HL7Exception { String inbound =
542             * this.xparser.encode(this.inboundTree.getMessage());
543             * this.inboundText.setText(inbound); }
544             */
545    
546            /**
547             * Opens a new window for displaying text (intended for displaying encoded
548             * messages.
549             */
550            public static void openTextWindow(String title, String text) {
551                    JFrame frame = new JFrame(title);
552    
553                    try {
554                            frame.getContentPane().setLayout(new BorderLayout());
555                            JTextArea textArea = new JTextArea(text);
556                            JScrollPane scroll = new JScrollPane(textArea);
557                            frame.getContentPane().add(scroll, BorderLayout.CENTER);
558    
559                            frame.pack();
560                            frame.setVisible(true);
561                    } catch (Exception e) {
562                            System.err.println("Can't display text in new window: "
563                                            + e.getMessage());
564                    }
565            }
566    
567            class SwingLogAppender extends WriterAppender {
568    
569                    private final JTextArea area;
570                    private ConcurrentLinkedQueue<String> buf;
571    
572                    public SwingLogAppender(JTextArea area) {
573                            super();
574                            this.area = area;
575                            this.buf = new ConcurrentLinkedQueue<String>();
576                            setLayout(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN));
577                    }
578    
579                    @Override
580                    public void append(LoggingEvent event) {
581                            buf.offer(getLayout().format(event));
582    
583                            if (getLayout().ignoresThrowable()) {
584                                    String[] exception = event.getThrowableStrRep();
585                                    if (exception != null) {
586                                            for (String line : exception) {
587                                                    buf.offer(line);
588                                                    buf.offer(Layout.LINE_SEP);
589                                            }
590                                    }
591                            }
592                            display();
593                    }
594    
595                    void display() {
596                            if (TestPanel.this.started && !buf.isEmpty()) {
597                                    SwingUtilities.invokeLater(new Runnable() {
598    
599                                            public void run() {
600                                                    while (!buf.isEmpty()) {
601                                                            area.append(buf.poll());
602                                                    }
603                                                    buf.clear();
604                                            }
605    
606                                    });
607                            }
608                    }
609    
610            }
611    
612            public static void main(String args[]) {
613    
614                    // BasicConfigurator.configure();
615                    if (args.length > 3) {
616                            System.out
617                                            .println("Usage: ca.uhn.hl7v2.app.TestPanel [tls] [inbound_port [outbound_port] ]");
618                            System.out
619                                            .println("    If port numbers are provided, an HL7Server will be started, to listen for incoming connections.");
620                            System.out
621                                            .println("    If outbound port is not provided, inbound and outbound messages will use the same port.");
622                            System.out
623                                            .println("    If tls is provided, inbound and outbound messages will be received/sent over TLS.");
624                            System.exit(1);
625                    }
626    
627                    // show a TestPanel in a window
628                    JFrame frame = new JFrame("Message Tester");
629    
630                    try {
631                            TestPanel panel = new TestPanel();
632                            boolean tls = false;
633                            int i = 0;
634    
635                            try {
636                                    if (args.length > 0) {
637                                            LowerLayerProtocol llp = LowerLayerProtocol.makeLLP();
638    
639                                            if ("tls".equals(args[i]) || "ssl".equals(args[i])) {
640                                                    tls = true;
641                                                    i++;
642                                            }
643    
644                                            int inPort = Integer.parseInt(args[i++]);
645                                            if (args.length > i) {
646                                                    int outPort = Integer.parseInt(args[i]);
647                                                    service = new TwoPortService(panel.getParser(), llp,
648                                                                    inPort, outPort, tls);
649                                            } else {
650                                                    service = new SimpleServer(inPort, llp,
651                                                                    panel.getParser(), tls);
652                                            }
653                                            service.registerConnectionListener(panel);
654                                            service.start();
655                                    }
656    
657                            } catch (NumberFormatException nfe) {
658                                    System.out
659                                                    .println("The given port number(s) are not valid integers");
660                                    System.exit(1);
661                            } catch (Exception e) {
662                                    e.printStackTrace();
663                            }
664    
665                            frame.getContentPane().add(panel, BorderLayout.CENTER);
666    
667                            // Finish setting up the frame
668                            frame.addWindowListener(new WindowAdapter() {
669                                    public void windowClosing(WindowEvent e) {
670                                            System.exit(0);
671                                    }
672                            });
673    
674                            frame.pack();
675                            frame.setVisible(true);
676                            panel.started = true;
677                            panel.appender.display();
678                    } catch (Exception e) {
679                            e.printStackTrace();
680                    }
681            }
682    
683    }