1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 */
18
19/**
20 * @author Aleksander V. Budniy
21 */
22
23/**
24 * Created on 25.11.2006
25 */
26package org.apache.harmony.jpda.tests.jdwp.Events;
27
28import java.io.IOException;
29import java.io.InputStream;
30import java.net.URL;
31
32import org.apache.harmony.jpda.tests.framework.LogWriter;
33import org.apache.harmony.jpda.tests.framework.TestErrorException;
34import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
35import org.apache.harmony.jpda.tests.share.SyncDebuggee;
36
37/**
38 * Debuggee for ClassUnloadTest unit test.
39 */
40public class ClassUnloadDebuggee extends SyncDebuggee {
41
42	public static final String TESTED_CLASS_NAME =
43		"org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTestedClass";
44
45	public static final int ARRAY_SIZE_FOR_MEMORY_STRESS = 1000000;
46
47	public static volatile boolean classUnloaded = false;
48
49	public static void main(String[] args) {
50        runDebuggee(ClassUnloadDebuggee.class);
51    }
52
53    public void run() {
54        logWriter.println("--> ClassUnloadDebuggee started");
55
56        // Test class prepare
57        logWriter.println("--> Load and prepare tested class");
58        CustomLoader loader = new CustomLoader(logWriter);
59
60        Class cls = null;
61        try {
62			cls = Class.forName(TESTED_CLASS_NAME, true, loader);
63	        logWriter.println("--> Tested class loaded: " + cls);
64		} catch (Exception e) {
65	        logWriter.println("--> Unable to load tested class: " + e);
66			throw new TestErrorException(e);
67		}
68
69        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
70        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
71
72        logWriter.println("--> Erase references to loaded class and its class loader");
73        classUnloaded = false;
74        cls = null;
75        loader = null;
76
77        logWriter.println("--> Create memory stress and start gc");
78        createMemoryStress(1000000, ARRAY_SIZE_FOR_MEMORY_STRESS);
79//        createMemoryStress(100000000, 1024);
80        System.gc();
81
82        String status = (classUnloaded ? "UNLOADED" : "LOADED");
83        logWriter.println("--> Class status after memory stress: " + status);
84        synchronizer.sendMessage(status);
85        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
86
87        logWriter.println("--> ClassUnloadDebuggee finished");
88    }
89
90    /*
91     * Stress algorithm for eating memory.
92     */
93    protected void createMemoryStress(int arrayLength_0, int arrayLength_1) {
94        Runtime currentRuntime = Runtime.getRuntime();
95        long freeMemory = currentRuntime.freeMemory();
96        logWriter.println
97        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
98
99        long[][] longArrayForCreatingMemoryStress = null;
100
101        int i = 0;
102        try {
103            longArrayForCreatingMemoryStress = new long[arrayLength_0][];
104            for (; i < longArrayForCreatingMemoryStress.length; i++) {
105                longArrayForCreatingMemoryStress[i] = new long[arrayLength_1];
106            }
107            logWriter.println("--> Debuggee: createMemoryStress: NO OutOfMemoryError!!!");
108        } catch ( OutOfMemoryError outOfMem ) {
109            longArrayForCreatingMemoryStress = null;
110            logWriter.println("--> Debuggee: createMemoryStress: OutOfMemoryError!!!");
111        }
112        freeMemory = currentRuntime.freeMemory();
113        logWriter.println
114        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
115
116        longArrayForCreatingMemoryStress = null;
117    }
118
119    /**
120     * More eager algorithm for eating memory.
121     */
122/*
123	protected void createMemoryStress(int maxChunkSize, int minChunkSize) {
124        Runtime currentRuntime = Runtime.getRuntime();
125        long freeMemory = currentRuntime.freeMemory();
126        logWriter.println
127        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
128
129        LinkedList list = new LinkedList();
130        int countOOM = 0;
131
132        for (int chunkSize = maxChunkSize; chunkSize >= minChunkSize; chunkSize /= 2) {
133        	try {
134                for (;;) {
135                	long[] chunk = new long[chunkSize];
136                	list.add(chunk);
137                }
138            } catch (OutOfMemoryError outOfMem) {
139                countOOM++;
140                System.gc();
141        	}
142        }
143
144        // enable to collect allocated memory
145        list = null;
146
147        freeMemory = currentRuntime.freeMemory();
148        logWriter.println
149        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
150
151        logWriter.println
152        ("--> Debuggee: createMemoryStress: OutOfMemoryError occured: " + countOOM);
153    }
154*/
155
156    /**
157     * Custom class loader to be used for tested class.
158     * It will be collected and finalized when tested class is unloaded.
159     */
160    static class CustomLoader extends ClassLoader {
161        private LogWriter logWriter;
162
163        public CustomLoader(LogWriter writer) {
164            this.logWriter = writer;
165        }
166
167        public Class<?> loadClass(String name) throws ClassNotFoundException {
168            if (TESTED_CLASS_NAME.equals(name)) {
169                // load only tested class with this loader
170                return findClass(name);
171            }
172            return getParent().loadClass(name);
173        }
174
175        public Class<?> findClass(String name) throws ClassNotFoundException {
176            try {
177                logWriter.println("-->> CustomClassLoader: Find class: " + name);
178	        	String res = name.replace('.', '/') + ".class";
179	            URL url = getResource(res);
180                logWriter.println("-->> CustomClassLoader: Found class file: " + res);
181	    		InputStream is = url.openStream();
182	            int size = 1024;
183	            byte bytes[] = new byte[size];
184	            int len = loadClassData(is, bytes, size);
185                logWriter.println("-->> CustomClassLoader: Loaded class bytes: " + len);
186	            Class cls = defineClass(name, bytes, 0, len);
187                logWriter.println("-->> CustomClassLoader: Defined class: " + cls);
188//	            resolveClass(cls);
189//                logWriter.println("-->> CustomClassLoader: Resolved class: " + cls);
190	            return cls;
191        	} catch (Exception e) {
192        		throw new ClassNotFoundException("Cannot load class: " + name, e);
193        	}
194        }
195
196        private int loadClassData(InputStream in, byte[] raw, int size) throws IOException {
197            int len = in.read(raw);
198            if (len >= size)
199                throw new IOException("Class file is too big: " + len);
200            in.close();
201            return len;
202        }
203
204        protected void finalize() throws Throwable {
205            logWriter.println("-->> CustomClassLoader: Class loader finalized => tested class UNLOADED");
206            ClassUnloadDebuggee.classUnloaded = true;
207       }
208    }
209}
210
211/**
212 * Internal class used in ClassUnloadTest
213 */
214class ClassUnloadTestedClass {
215}
216