001/* An application for editing ptolemy models visually.
002
003 Copyright (c) 1999-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027 */
028package ptolemy.vergil;
029
030import java.io.File;
031import java.io.IOException;
032import java.net.URL;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036
037import javax.swing.SwingUtilities;
038
039import ptolemy.actor.gui.ActorGraphicalMessageHandler;
040import ptolemy.actor.gui.Configuration;
041import ptolemy.actor.gui.Effigy;
042import ptolemy.actor.gui.EffigyFactory;
043import ptolemy.actor.gui.MoMLApplication;
044import ptolemy.actor.gui.ModelDirectory;
045import ptolemy.actor.gui.PtolemyEffigy;
046import ptolemy.actor.gui.PtolemyPreferences;
047import ptolemy.actor.gui.UserActorLibrary;
048import ptolemy.data.expr.Parameter;
049import ptolemy.gui.PtGUIUtilities;
050import ptolemy.kernel.util.IllegalActionException;
051import ptolemy.kernel.util.InternalErrorException;
052import ptolemy.moml.MoMLParser;
053import ptolemy.moml.filter.RemoveNonPtinyClasses;
054import ptolemy.util.MessageHandler;
055import ptolemy.util.StringUtilities;
056
057///////////////////////////////////////////////////////////////////
058//// VergilApplication
059
060/**
061 An application for editing ptolemy models visually.
062
063 <p>
064 The exact facilities that are available are determined by an optional
065 command line argument that names a directory in ptolemy/configs that
066 contains a configuration.xml file.  For example, if we call vergil
067 -ptiny, then we will use ptolemy/configs/ptiny/configuration.xml and
068 ptolemy/configs/ptiny/intro.htm.  The default configuration is
069 ptolemy/configs/full/configuration.xml, which is loaded before any
070 other command-line arguments are processed.</p>
071
072 <p>This application also takes an optional command line argument pair
073 <code>-configuration <i>configurationFile.xml</i></code> that names a configuration
074 to be read.  For example,
075 <pre>
076 $PTII/bin/vergil -configuration ptolemy/configs/ptiny/configuration.xml
077 </pre>
078 and
079 <pre>
080 $PTII/bin/vergil -ptiny
081 </pre>
082 are equivalent
083 <p>
084 If there are no command-line arguments at all, then the configuration
085 file is augmented by the MoML file ptolemy/configs/full/welcomeWindow.xml</p>
086
087 <p>Note that if the configuration starts with "ptiny", then
088 {@link ptolemy.moml.filter.RemoveNonPtinyClasses} is used to remove classes
089 such as code generators that might be present in the model but are not part
090 of the Ptiny configuration.</p>
091
092 @author Edward A. Lee, Steve Neuendorffer, Christopher Hylands, contributor: Chad Berkeley
093 @version $Id$
094 @since Ptolemy II 1.0
095 @Pt.ProposedRating Yellow (eal)
096 @Pt.AcceptedRating Red (eal)
097 @see ptolemy.actor.gui.ModelFrame
098 @see ptolemy.actor.gui.RunTableau
099 @see ptolemy.actor.gui.PtExecuteApplication
100 */
101public class VergilApplication extends MoMLApplication {
102    /** Parse the specified command-line arguments, creating models
103     *  and frames to interact with them.
104     *  Look for configurations in "ptolemy/configs"
105     *  @param args The command-line arguments.
106     *  @exception Exception If command line arguments have problems.
107     */
108    public VergilApplication(String[] args) throws Exception {
109        // FindBugs: FIXME: Note that MoMLApplication(String, String[]) starts a
110        // thread, which used to mean that the error handler will not be registered before
111        // the thread starts.
112        this("ptolemy/configs", args);
113    }
114
115    /** Parse the specified command-line arguments, creating models
116     *  and frames to interact with them.
117     *  @param basePath The basePath to look for configurations
118     *  in, usually "ptolemy/configs", but other tools might
119     *  have other configurations in other directories
120     *  @param args The command-line arguments.
121     *  @exception Exception If command line arguments have problems.
122     */
123    public VergilApplication(String basePath, String[] args) throws Exception {
124        // FindBugs: FIXME: Note that MoMLApplication(String,
125        // String[]) starts a thread, which used to mean that the
126        // message handler and error handler were not registered
127        // before the thread starts.
128        super("ptolemy/configs", args, new VergilGraphicalMessageHandler(),
129                new VergilErrorHandler());
130        try {
131            // Avoid (cd $PTII/doc/test/junit; make) throwing an exception under Java 1.8
132            // under Mac OS X and Linux.  Also, the colors are wrong under Mac OS X 10.9.
133            // Another way to get this: "occurred just building a simple SDF model... Open a few libraries and perform a search. "
134            // The exception is:
135            //    java.awt.color.CMMException: LCMS error 13: Couldn't link the profiles
136            //         at sun.java2d.cmm.lcms.LCMS.createNativeTransform(Native Method)
137            //         at sun.java2d.cmm.lcms.LCMS.createTransform(LCMS.java:156)
138            //         at sun.java2d.cmm.lcms.LCMSTransform.doTransform(LCMSTransform.java:155)
139            //         at sun.java2d.cmm.lcms.LCMSTransform.colorConvert(LCMSTransform.java:629)
140            //         at java.awt.color.ICC_ColorSpace.toRGB(ICC_ColorSpace.java:182)
141            //         at com.sun.pdfview.colorspace.PDFColorSpace.getPaint(PDFColorSpace.java:222)
142            //         at com.sun.pdfview.PDFParser.iterate(PDFParser.java:656)
143            //         at com.sun.pdfview.BaseWatchable.run(BaseWatchable.java:101)
144            //         at java.lang.Thread.run(Thread.java:745)
145            // See https://wiki.eecs.berkeley.edu/ptexternal/Main/Main/PDF-renderer
146            // and https://stackoverflow.com/questions/26535842/multithreaded-jpeg-image-processing-in-java
147
148            // This code was in ptolemy/vergil/pdfrenderer/PDFIcon.java, but the problem persisted
149            // so we are probably invoking ICC_ColorSpace via ImageIO somewhere.
150            Class.forName("javax.imageio.ImageIO");
151            Class.forName("java.awt.color.ICC_ColorSpace");
152
153            // This class is not present under Linux?
154            try {
155                Class.forName("sun.java2d.cmm.lcms.LCMS");
156            } catch (Throwable throwable) {
157                // Ignore.
158            }
159        } catch (Throwable throwable) {
160            new IllegalActionException(null, throwable,
161                    "Could not instantiate a Java2d class?"
162                            + "See https://wiki.eecs.berkeley.edu/ptexternal/Main/Main/PDF-renderer")
163                                    .printStackTrace();
164        }
165    }
166
167    ///////////////////////////////////////////////////////////////////
168    ////                         public methods                    ////
169
170    /** Print out an error message and stack trace on stderr and then
171     * display a dialog box.  This method is used as a fail safe
172     * in case there are problems with the configuration
173     * We use a Throwable here instead of an Exception because
174     * we might get an Error or and Exception. For example, if we
175     * are using JNI, then we might get a java.lang.UnsatisfiedLinkError,
176     * which is an Error, not and Exception.
177     * @param message  The message to be displayed
178     * @param args The arguments  to be displayed
179     * @param throwable The Throwable that caused the problem.
180     */
181    public static void errorAndExit(String message, String[] args,
182            Throwable throwable) {
183        // This is public so that other application frameworks may
184        // invoke it
185        StringBuffer argsBuffer = new StringBuffer("Command failed");
186
187        if (args.length > 0) {
188            argsBuffer.append("\nArguments: " + args[0]);
189
190            for (int i = 1; i < args.length; i++) {
191                argsBuffer.append(" " + args[i]);
192            }
193
194            argsBuffer.append("\n");
195        }
196
197        // First, print out the stack trace so that
198        // if the next step fails the user has
199        // a chance of seeing the message.
200        System.out.println(argsBuffer.toString());
201        throwable.printStackTrace();
202
203        // Display the error message in a stack trace
204        // If there are problems with the configuration,
205        // then there is a chance that we have not
206        // registered the GraphicalMessageHandler yet
207        // so we do so now so that we are sure
208        // the user can see the message.
209        // One way to test this is to run vergil -configuration foo
210        MessageHandler.setMessageHandler(new ActorGraphicalMessageHandler());
211
212        MessageHandler.error(argsBuffer.toString(), throwable);
213
214        try {
215            StringUtilities.exit(0);
216        } catch (SecurityException ex) {
217            System.out.println("Warning: Failed to call System.exit()."
218                    + "(-sandbox always causes this)");
219            System.out.println("About to clean configurations");
220            Iterator configurations = Configuration.configurations().iterator();
221            while (configurations.hasNext()) {
222                Configuration configuration = (Configuration) configurations
223                        .next();
224                try {
225                    System.out.println("Setting container of "
226                            + configuration.getFullName() + " to null");
227                    configuration.setContainer(null);
228                } catch (Exception ex2) {
229                    ex2.printStackTrace();
230                }
231            }
232
233        }
234    }
235
236    /** Create a new instance of this application, passing it the
237     *  command-line arguments.
238     *  @param args The command-line arguments.
239     */
240    public static void main(final String[] args) {
241        if (PtGUIUtilities.macOSLookAndFeel()) {
242            String aboutNameProperty = "com.apple.mrj.application.apple.menu.about.name";
243            try {
244                // Mac OS X: Set the "About" menu name.  These calls must be made outside
245                // the Swing Event Thread.
246                System.setProperty(aboutNameProperty, "Vergil");
247            } catch (SecurityException ex) {
248                if (!_printedSecurityExceptionMessage) {
249                    _printedSecurityExceptionMessage = true;
250                    System.out.println("Warning: Mac OS X: Failed to set the \""
251                            + aboutNameProperty + "\" property. "
252                            + "(applets and -sandbox always causes this)");
253                }
254            }
255            // Uncomment the next line to use the screen menu bar instead of a per-window
256            // menu bar.
257            //System.setProperty("apple.laf.useScreenMenuBar", "true");
258        }
259
260        // FIXME: Java superstition dictates that if you want something
261        // to work, you should invoke it in event thread.  Otherwise,
262        // weird things happens at the user interface level.  This
263        // seems to prevent occasional errors rending HTML under Web Start.
264        try {
265            // NOTE: This is unfortunate... It would be nice
266            // if this could be run inside a PtolemyThread, since
267            // getting read access the workspace is much more efficient
268            // in PtolemyThread.
269            SwingUtilities.invokeLater(new Runnable() {
270                @Override
271                public void run() {
272                    try {
273                        new VergilApplication(args);
274                    } catch (Throwable throwable) {
275                        // If we get an Error or and Exception while
276                        // configuring, we will end up here.
277                        errorAndExit("Command failed", args, throwable);
278                    }
279                }
280            });
281        } catch (Throwable throwable2) {
282            // We are not likely to get here, but just to be safe
283            // we try to print the error message and display it in a
284            // graphical widget.
285            errorAndExit("Command failed", args, throwable2);
286        }
287
288        // If the -test arg was set, then exit after 2 seconds.
289        if (_test) {
290            try {
291                Thread.sleep(2000);
292            } catch (InterruptedException e) {
293            }
294
295            StringUtilities.exit(0);
296        }
297    }
298
299    /**
300     *  Open the MoML file at the given location as a new library in the
301     *  actor library for this application.
302     *
303     *  An alternate class can be used to build the library if reading the
304     *  MoML is not desired.  The class must extend ptolemy.moml.LibraryBuilder
305     *  and the _alternateLibraryBuilder property must be set with the 'value'
306     *  set to the class that extends LibraryBuilder.
307     *
308     *  @param configuration The configuration where we look for the
309     *  actor library.
310     *  @param file The MoML file to open.
311     *  @exception Exception If there is a problem opening the configuration,
312     *  opening the MoML file, or opening the MoML file as a new library.
313     *  @deprecated Use {@link ptolemy.actor.gui.UserActorLibrary#openLibrary(Configuration, File)}
314     */
315    @Deprecated
316    public static void openLibrary(Configuration configuration, File file)
317            throws Exception {
318        MoMLParser.setErrorHandler(new VergilErrorHandler());
319        UserActorLibrary.openLibrary(configuration, file);
320    }
321
322    ///////////////////////////////////////////////////////////////////
323    ////                         protected methods                 ////
324
325    /** Return a default Configuration.  The initial default configuration
326     *  is the MoML file full/configuration.xml under the _basePath
327     *  directory, which is usually ptolemy/configs.
328     *  using different command line arguments can change the value
329     *  Usually, we also open the user library, which is located
330     *  in the directory returned by
331     *  {@link ptolemy.util.StringUtilities#preferencesDirectory()}
332     *  If the configuration contains a top level Parameter named
333     *  _hideUserLibrary, then we do not open the user library.
334     *
335     *  @return A default configuration.
336     *  @exception Exception If the configuration cannot be opened.
337     */
338    @Override
339    protected Configuration _createDefaultConfiguration() throws Exception {
340        try {
341            if (_configurationURL == null) {
342                _configurationURL = specToURL(
343                        _basePath + "/full/configuration.xml");
344            }
345        } catch (IOException ex) {
346            try {
347                // If we ship HyVisual without a full installation, then
348                // we try the hyvisual configuration.
349                // vergil -help needs this.
350                // FIXME: we could do better than this and either
351                // search for configurations or go through a list
352                // of them.
353                _configurationURL = specToURL(
354                        _basePath + "/hyvisual/configuration.xml");
355            } catch (IOException ex2) {
356                // Throw the original exception.
357                throw ex;
358            }
359        }
360
361        // This has the side effects of merging properties from ptII.properties
362        Configuration configuration = super._createDefaultConfiguration();
363
364        try {
365            configuration = readConfiguration(_configurationURL);
366        } catch (Exception ex) {
367            throw new Exception(
368                    "Failed to read configuration '" + _configurationURL + "'",
369                    ex);
370        }
371
372        // Read the user preferences, if any.
373        PtolemyPreferences.setDefaultPreferences(configuration);
374
375        // If _hideUserLibraryAttribute is not present, or is false,
376        // call openUserLibrary().  openUserLibrary() will open either the
377        // user library or the library named by the _alternateLibraryBuilder.
378        Parameter hideUserLibraryAttribute = (Parameter) configuration
379                .getAttribute("_hideUserLibrary", Parameter.class);
380
381        if (hideUserLibraryAttribute == null
382                || hideUserLibraryAttribute.getExpression().equals("false")) {
383
384            // Load the user library.
385            try {
386                MoMLParser.setErrorHandler(new VergilErrorHandler());
387                UserActorLibrary.openUserLibrary(configuration);
388            } catch (Exception ex) {
389                MessageHandler.error("Failed to display user library.", ex);
390            }
391        }
392
393        return configuration;
394    }
395
396    /** Return a default Configuration to use when there are no command-line
397     *  arguments. If the configuration contains a parameter
398     *  _applicationBlankPtolemyEffigyAtStartup
399     *  then we create an empty up an empty PtolemyEffigy.
400     *  @return A configuration for when there no command-line arguments.
401     *  @exception Exception If the configuration cannot be opened.
402     */
403    @Override
404    protected Configuration _createEmptyConfiguration() throws Exception {
405        Configuration configuration = _createDefaultConfiguration();
406        URL welcomeURL = null;
407        URL introURL = null;
408
409        ModelDirectory directory = configuration.getDirectory();
410
411        Parameter applicationBlankPtolemyEffigyAtStartup = (Parameter) configuration
412                .getAttribute("_applicationBlankPtolemyEffigyAtStartup",
413                        Parameter.class);
414        if (applicationBlankPtolemyEffigyAtStartup != null
415                && applicationBlankPtolemyEffigyAtStartup.getExpression()
416                        .equals("true")) {
417
418            EffigyFactory factory = null;
419
420            Parameter startupEffigyFactoryName = (Parameter) applicationBlankPtolemyEffigyAtStartup
421                    .getAttribute("_startupEffigyFactoryName");
422
423            // See if there's a startup name
424            if (startupEffigyFactoryName != null) {
425                String startupName = startupEffigyFactoryName.getExpression();
426                EffigyFactory factoryContainer = (EffigyFactory) configuration
427                        .getEntity("effigyFactory");
428
429                // Make sure there is effigyFactory
430                if (factoryContainer == null) {
431                    throw new IllegalActionException(
432                            "Could not find effigyFactory.");
433                }
434
435                // Search through the effigy factories for the startup name.
436                List factoryList = factoryContainer
437                        .entityList(EffigyFactory.class);
438                Iterator factories = factoryList.iterator();
439
440                while (factories.hasNext()) {
441                    final EffigyFactory currentFactory = (EffigyFactory) factories
442                            .next();
443                    if (currentFactory.getName().equals(startupName)) {
444                        factory = currentFactory;
445                    }
446                }
447
448                // Make sure we found it.
449                if (factory == null) {
450                    throw new IllegalActionException(
451                            "Could not find effigy factory " + startupName);
452                }
453
454            } else {
455                factory = new PtolemyEffigy.Factory(directory,
456                        directory.uniqueName("ptolemyEffigy"));
457            }
458            Effigy effigy = factory.createEffigy(directory, null, null);
459            configuration.createPrimaryTableau(effigy);
460        }
461
462        try {
463            // First, we see if we can find the welcome window by
464            // looking at the default configuration.
465            // FIXME: this seems wrong, we should be able to get
466            // an attribute from the configuration that names the
467            // welcome window.
468            String configurationURLString = _configurationURL.toExternalForm();
469            String base = configurationURLString.substring(0,
470                    configurationURLString.lastIndexOf("/"));
471
472            welcomeURL = specToURL(base + "/welcomeWindow.xml");
473            introURL = specToURL(base + "/intro.htm");
474            _parser.reset();
475            _parser.setContext(configuration);
476            _parser.parse(welcomeURL, welcomeURL);
477        } catch (Throwable throwable) {
478            // OK, that did not work, try a different method.
479            if (_configurationSubdirectory == null) {
480                _configurationSubdirectory = "full";
481            }
482
483            // FIXME: This code is Dog slow for some reason.
484            welcomeURL = specToURL(_basePath + "/" + _configurationSubdirectory
485                    + "/welcomeWindow.xml");
486            introURL = specToURL(_basePath + "/" + _configurationSubdirectory
487                    + "/intro.htm");
488            _parser.reset();
489            _parser.setContext(configuration);
490            _parser.parse(welcomeURL, welcomeURL);
491        }
492
493        Effigy doc = (Effigy) configuration.getEntity("directory.doc");
494
495        if (doc == null) {
496            throw new InternalErrorException(
497                    "Configuration does not have a directory.doc entity?");
498        }
499        doc.identifier.setExpression(introURL.toExternalForm());
500
501        return configuration;
502    }
503
504    /** Parse the command-line arguments.
505     *  @exception Exception If an argument is not understood or triggers
506     *   an error.
507     */
508    @Override
509    protected void _parseArgs(final String[] args) throws Exception {
510        _commandTemplate = "vergil [ options ] [file ...]";
511
512        // VergilApplication.super._parseArgs(args)
513        // calls _createDefaultConfiguration() asap,
514        // so delay calling it until we process
515        // the arguments and possibly get a configuration.
516        List processedArgsList = new LinkedList();
517
518        for (int i = 0; i < args.length; i++) {
519            // Parse any configuration specific args first so that we can
520            // set up the configuration for later use by the parent class.
521            if (!_configurationParseArg(args[i])) {
522                // If we did not parse the arg, the
523                // add it to the list of args to be passed
524                // to the super class.
525                processedArgsList.add(args[i]);
526            }
527        }
528
529        if (_expectingConfiguration) {
530            throw new IllegalActionException("Missing configuration");
531        }
532
533        String[] processedArgs = (String[]) processedArgsList
534                .toArray(new String[processedArgsList.size()]);
535
536        super._parseArgs(processedArgs);
537    }
538
539    /** Return a string summarizing the command-line arguments.
540     *  @return A usage string.
541     */
542    @Override
543    protected String _usage() {
544        return _configurationUsage(_commandTemplate, _commandOptions,
545                new String[] {});
546    }
547
548    ///////////////////////////////////////////////////////////////////
549    ////                         protected variables               ////
550    // _commandOptions is static because
551    // _usage() may be called from the constructor of the parent class,
552    //  in which case non-static variables are null?
553
554    /** The command-line options that take arguments. */
555    protected static String[][] _commandOptions = { { "-configuration",
556            "<configuration URL, defaults to ptolemy/configs/full/configuration.xml>" }, };
557
558    ///////////////////////////////////////////////////////////////////
559    ////                         private methods                   ////
560
561    /** Parse a command-line argument.  Usually, we would name this
562     *  method _parseArg(), but we want to handle any arguments that
563     *  handle configuration changes before calling the parent class
564     *  _parseArg() because the parent class depends either having a
565     *  configuration to work with, or the parent class sets up a
566     *  configuration.
567     *  @return True if the argument is understood, false otherwise.
568     *  @exception Exception If something goes wrong.
569     */
570    private boolean _configurationParseArg(String arg) throws Exception {
571        if (arg.startsWith("-conf")) {
572            _expectingConfiguration = true;
573        } else if (arg.startsWith("-")) {
574            // If the argument names a directory in ptolemy/configs
575            // that contains a file named configuration.xml that can
576            // be found either as a URL or in the classpath, then
577            // assume that it is a configuration.  For example, -ptiny
578            // will look for ptolemy/configs/ptiny/configuration.xml
579            // If the argument does not name a configuration, then
580            // we return false so that the argument can be processed
581            // by the parent class.
582            try {
583                _configurationSubdirectory = arg.substring(1);
584
585                String potentialConfiguration = _basePath + "/"
586                        + _configurationSubdirectory + "/configuration.xml";
587
588                // This will throw an Exception if we can't find the config.
589                _configurationURL = specToURL(potentialConfiguration);
590                if (_configurationSubdirectory.startsWith("ptiny")) {
591                    // If the configuration startes with ptiny, then
592                    // filter out codegenerators
593                    LinkedList filters = new LinkedList();
594                    filters.add(new RemoveNonPtinyClasses());
595                    MoMLParser.addMoMLFilters(filters);
596                }
597            } catch (Throwable ex) {
598                // The argument did not name a configuration, let the parent
599                // class have a shot.
600                return false;
601            }
602        } else if (_expectingConfiguration) {
603            _expectingConfiguration = false;
604            _configurationURL = specToURL(arg);
605        } else {
606            return false;
607        }
608
609        return true;
610    }
611
612    ///////////////////////////////////////////////////////////////////
613    ////                         private variables                 ////
614    // The subdirectory (if any) of ptolemy/configs where the configuration
615    // may be found.  For example if vergil was called with -ptiny,
616    // then this variable will be set to "ptiny", and the configuration
617    // should be at ptolemy/configs/ptiny/configuration.xml
618    private String _configurationSubdirectory;
619
620    // URL of the configuration to read.
621    // The URL may absolute, or relative to the Ptolemy II tree root.
622    // We use the URL instead of the string so that if the configuration
623    // is set as a command line argument, we can use the processed value
624    // from the command line instead of calling specToURL() again, which
625    // might be expensive.
626    private URL _configurationURL;
627
628    // Flag indicating that the previous argument was -conf
629    private boolean _expectingConfiguration = false;
630
631    /** True if we have printed the securityExceptionMessage. */
632    private static boolean _printedSecurityExceptionMessage = false;
633}