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}