001/* Test unloading a model
002 Copyright (c) 2010-2016 The Regents of the University of California.
003 All rights reserved.
004 Permission is hereby granted, without written agreement and without
005 license or royalty fees, to use, copy, modify, and distribute this
006 software and its documentation for any purpose, provided that the above
007 copyright notice and the following two paragraphs appear in all copies
008 of this software.
009
010 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
011 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
012 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
013 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
014 SUCH DAMAGE.
015
016 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
017 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
018 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
019 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
020 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
021 ENHANCEMENTS, OR MODIFICATIONS.
022
023 PT_COPYRIGHT_VERSION_2
024 COPYRIGHTENDKEY
025
026 */
027package ptolemy.moml.test;
028
029import java.io.File;
030
031import ptolemy.actor.CompositeActor;
032import ptolemy.actor.Manager;
033import ptolemy.kernel.util.Workspace;
034import ptolemy.moml.MoMLParser;
035import ptolemy.moml.MoMLSimpleApplication;
036import ptolemy.moml.filter.BackwardCompatibility;
037import ptolemy.moml.filter.RemoveGraphicalClasses;
038import ptolemy.util.StringUtilities;
039
040/**
041 Test unloading a model.
042
043 <p>This class is used to test loading and unlooading a model.
044 When used with a memory profiler like JProfiler, we can
045 look for leaks.  To run the test, use:</p>
046 <pre>
047java -classpath $PTII ptolemy.moml.test.UnloadModelTest ../demo/test.xml
048 </pre>
049
050
051 @author Brian Hudson, Christopher Brooks
052 @version $Id$
053 @since Ptolemy II 10.0
054 @Pt.ProposedRating Red (cxh)
055 @Pt.AcceptedRating Red (cxh)
056 */
057public class UnloadModelTest extends MoMLSimpleApplication {
058
059    /** Parse the xml file and run it.
060     *  @param xmlFileName A string that refers to an MoML file that
061     *  contains a Ptolemy II model.  The string should be
062     *  a relative pathname.
063     *  @exception Throwable If there was a problem parsing
064     *  or running the model.
065     */
066    public UnloadModelTest(String xmlFileName) throws Throwable {
067        workspace = new Workspace("MyWorkspace");
068        parser = new MoMLParser(workspace);
069
070        // The test suite calls MoMLSimpleApplication multiple times,
071        // and the list of filters is static, so we reset it each time
072        // so as to avoid adding filters every time we run an auto test.
073        // We set the list of MoMLFilters to handle Backward Compatibility.
074        MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters());
075
076        // Filter out any graphical classes.
077        MoMLParser.addMoMLFilter(new RemoveGraphicalClasses());
078
079        // If there is a MoML error, then throw the exception as opposed
080        // to skipping the error.  If we call StreamErrorHandler instead,
081        // then the nightly build may fail to report MoML parse errors
082        // as failed tests
083        //parser.setErrorHandler(new StreamErrorHandler());
084        // We use parse(URL, URL) here instead of parseFile(String)
085        // because parseFile() works best on relative pathnames and
086        // has problems finding resources like files specified in
087        // parameters if the xml file was specified as an absolute path.
088        toplevel = (CompositeActor) parser.parse(null,
089                new File(xmlFileName).toURI().toURL());
090
091        _manager = new Manager(toplevel.workspace(), "MoMLSimpleApplication");
092        toplevel.setManager(_manager);
093        toplevel.addChangeListener(this);
094
095        _manager.addExecutionListener(this);
096        _activeCount++;
097
098        _manager.startRun();
099
100        Thread waitThread = new UnloadThread();
101
102        // Note that we start the thread here, which could
103        // be risky when we subclass, since the thread will be
104        // started before the subclass constructor finishes (FindBugs)
105        waitThread.start();
106        waitThread.join();
107        if (_sawThrowable != null) {
108            throw _sawThrowable;
109        }
110    }
111
112    /** Load a model and then unload it.
113     *  <p>Typically, this class is invoked with something like:
114     *  <pre>
115     *  java -classpath $PTII ptolemy.moml.test.UnloadModelTest ../demo/test.xml
116     *  </pre>
117     *  @param args The first argument is the name of the file to be loaded.
118     */
119    public static void main(String[] args) {
120        try {
121            new UnloadModelTest(args[0]);
122        } catch (Throwable ex) {
123            System.err.println("Command failed: " + ex);
124            ex.printStackTrace();
125            StringUtilities.exit(1);
126        }
127    }
128
129    /** The MoMLParser that is created and then destroyed. */
130    public MoMLParser parser;
131
132    /** The toplevel model that is created and then destroyed. */
133    public CompositeActor toplevel;
134
135    /** The workspace in which the model and Manager are created. */
136    public Workspace workspace;
137
138    /** Return the amount of memory used.
139     *  @return A string that describes the amount of memory.
140     */
141    public static String memory() {
142        Runtime runtime = Runtime.getRuntime();
143        long totalMemory = runtime.totalMemory() / 1024;
144        long freeMemory = runtime.freeMemory() / 1024;
145        return "Memory: " + totalMemory + "K Free: " + freeMemory + "K ("
146                + Math.round((double) freeMemory / (double) totalMemory * 100.0)
147                + "%)";
148    }
149
150    /** Wait for the run to finish and the unload the model.
151     */
152    public class UnloadThread extends Thread {
153        @Override
154        public void run() {
155            waitForFinish();
156            try {
157                // First, we gc and then print the memory stats
158                // BTW to get more info about gc,
159                // use java -verbose:gc . . .
160                System.gc();
161                Thread.sleep(1000);
162                System.out.println("Memory before unloading: " + memory());
163
164                //              if (toplevel instanceof CompositeEntity) {
165                //                  try {
166                //                      ParserAttribute parserAttribute = (ParserAttribute) toplevel
167                //                          .getAttribute("_parser", ParserAttribute.class);
168                //                      parserAttribute.setContainer(null);
169                //                      ((CompositeEntity)toplevel).setContainer(null);
170                //                  } catch (Exception ex) {
171                //                      ex.printStackTrace();
172                //                  }
173                //              }
174
175                if (parser != null) {
176                    //                  if (parser.topObjectsCreated() != null) {
177                    //                      parser.topObjectsCreated().remove(toplevel);
178                    //                  }
179
180                    parser.resetAll();
181                    // parser is a public variable so setting it to
182                    // null will (hopefully) cause the garbage
183                    // collector to collect it.
184                    parser = null;
185
186                    // The next line removes the static backward compatibility
187                    // filters, which is probably not what we want if we
188                    // want to parse another file.
189                    // BackwardCompatibility.clear();
190
191                    // The next line will remove the static MoMLParser
192                    // used by the filters.  If we add filters, then the static
193                    // MoMLParser is recreated.
194                    MoMLParser.setMoMLFilters(null);
195                }
196
197                //                 try {
198                //                     toplevel.setContainer(null);
199                //                     _manager.terminate();
200                //                     toplevel.setManager(null);
201                //                 } catch (Exception ex) {
202                //                     ex.printStackTrace();
203                //                 }
204
205                // _manager is a protected variable so setting it to
206                // null will (hopefully) cause the garbage
207                // collector to collect it.
208                _manager = null;
209
210                //                 try {
211                //                     toplevel.workspace().getWriteAccess();
212                //                     toplevel.workspace().removeAll();
213                //                 } catch (Throwable throwable) {
214                //                     throwable.printStackTrace();
215                //                 } finally {
216                //                     toplevel.workspace().doneWriting();
217                //                 }
218
219                // toplevel and workspace are a public variables so
220                // setting it to null will (hopefully) cause the
221                // garbage collector to collect them
222
223                // Set toplevel to null so that the Manager is collected.
224                toplevel = null;
225
226                // Set workspace to null so that the objects contained
227                // by the workspace may be collected.
228                workspace = null;
229
230                System.gc();
231                Thread.sleep(1000);
232                System.out.println("Memory after  unloading: " + memory());
233
234                System.out.println("Sleeping for 1000 seconds");
235                if (_sawThrowable != null) {
236                    throw new RuntimeException("Execution failed",
237                            _sawThrowable);
238                }
239                Thread.sleep(1000000);
240            } catch (InterruptedException ex) {
241                throw new RuntimeException("InterrupteException", ex);
242            }
243        }
244    }
245}