15f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/*
25f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * Licensed to the Apache Software Foundation (ASF) under one or more
35f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * contributor license agreements.  See the NOTICE file distributed with
45f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * this work for additional information regarding copyright ownership.
55f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * The ASF licenses this file to You under the Apache License, Version 2.0
65f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * (the "License"); you may not use this file except in compliance with
75f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * the License.  You may obtain a copy of the License at
85f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *
95f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *     http://www.apache.org/licenses/LICENSE-2.0
105f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *
115f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *  Unless required by applicable law or agreed to in writing, software
125f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *  distributed under the License is distributed on an "AS IS" BASIS,
135f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
145f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *
155f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *  See the License for the specific language governing permissions and
165f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes *  limitations under the License.
175f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes */
185f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
195f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/**
205f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * @author Aleksander V. Budniy
215f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes */
225f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
235f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/**
245f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * Created on 25.11.2006
255f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes */
265f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughespackage org.apache.harmony.jpda.tests.jdwp.Events;
275f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
285f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport java.io.IOException;
295f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport java.io.InputStream;
305f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport java.net.URL;
315f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
325f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport org.apache.harmony.jpda.tests.framework.LogWriter;
335f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport org.apache.harmony.jpda.tests.framework.TestErrorException;
345f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
355f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesimport org.apache.harmony.jpda.tests.share.SyncDebuggee;
365f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
375f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/**
385f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * Debuggee for ClassUnloadTest unit test.
395f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes */
405f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughespublic class ClassUnloadDebuggee extends SyncDebuggee {
415f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
425f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	public static final String TESTED_CLASS_NAME =
435f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes		"org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTestedClass";
445f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
455f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	public static final int ARRAY_SIZE_FOR_MEMORY_STRESS = 1000000;
465f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
475f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	public static volatile boolean classUnloaded = false;
485f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
495f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	public static void main(String[] args) {
505f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        runDebuggee(ClassUnloadDebuggee.class);
515f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    }
52e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz
53e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz    @Override
545f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    public void run() {
555f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> ClassUnloadDebuggee started");
565f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
575f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        // Test class prepare
585f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> Load and prepare tested class");
595f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        CustomLoader loader = new CustomLoader(logWriter);
60e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz
61e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz        Class<?> cls = null;
625f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        try {
635f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes			cls = Class.forName(TESTED_CLASS_NAME, true, loader);
645f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	        logWriter.println("--> Tested class loaded: " + cls);
655f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes		} catch (Exception e) {
665f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	        logWriter.println("--> Unable to load tested class: " + e);
675f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes			throw new TestErrorException(e);
685f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes		}
695f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
705f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
715f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
725f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
735f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> Erase references to loaded class and its class loader");
745f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        classUnloaded = false;
755f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        cls = null;
765f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        loader = null;
775f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
785f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> Create memory stress and start gc");
795f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        createMemoryStress(1000000, ARRAY_SIZE_FOR_MEMORY_STRESS);
805f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes//        createMemoryStress(100000000, 1024);
815f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        System.gc();
825f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
835f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        String status = (classUnloaded ? "UNLOADED" : "LOADED");
845f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> Class status after memory stress: " + status);
855f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        synchronizer.sendMessage(status);
865f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
875f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
885f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println("--> ClassUnloadDebuggee finished");
895f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    }
905f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
915f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    /*
925f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     * Stress algorithm for eating memory.
935f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     */
945f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    protected void createMemoryStress(int arrayLength_0, int arrayLength_1) {
955f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        Runtime currentRuntime = Runtime.getRuntime();
965f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        long freeMemory = currentRuntime.freeMemory();
975f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println
985f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
995f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1005f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        long[][] longArrayForCreatingMemoryStress = null;
1015f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1025f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        int i = 0;
1035f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        try {
1045f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            longArrayForCreatingMemoryStress = new long[arrayLength_0][];
1055f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            for (; i < longArrayForCreatingMemoryStress.length; i++) {
1065f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                longArrayForCreatingMemoryStress[i] = new long[arrayLength_1];
1075f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            }
1085f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            logWriter.println("--> Debuggee: createMemoryStress: NO OutOfMemoryError!!!");
1095f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        } catch ( OutOfMemoryError outOfMem ) {
1105f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            longArrayForCreatingMemoryStress = null;
1115f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            logWriter.println("--> Debuggee: createMemoryStress: OutOfMemoryError!!!");
1125f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
1135f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        freeMemory = currentRuntime.freeMemory();
1145f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println
1155f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
1165f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1175f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        longArrayForCreatingMemoryStress = null;
1185f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    }
1195f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1205f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    /**
1215f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     * More eager algorithm for eating memory.
1225f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     */
1235f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/*
1245f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	protected void createMemoryStress(int maxChunkSize, int minChunkSize) {
1255f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        Runtime currentRuntime = Runtime.getRuntime();
1265f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        long freeMemory = currentRuntime.freeMemory();
1275f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println
1285f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        ("--> Debuggee: createMemoryStress: freeMemory (bytes) before memory stress = " + freeMemory);
1295f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1305f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        LinkedList list = new LinkedList();
1315f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        int countOOM = 0;
1325f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1335f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        for (int chunkSize = maxChunkSize; chunkSize >= minChunkSize; chunkSize /= 2) {
1345f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        	try {
1355f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                for (;;) {
1365f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                	long[] chunk = new long[chunkSize];
1375f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                	list.add(chunk);
1385f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                }
1395f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            } catch (OutOfMemoryError outOfMem) {
1405f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                countOOM++;
1415f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                System.gc();
1425f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        	}
1435f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
1445f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1455f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        // enable to collect allocated memory
1465f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        list = null;
1475f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1485f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        freeMemory = currentRuntime.freeMemory();
1495f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println
1505f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        ("--> Debuggee: createMemoryStress: freeMemory after creating memory stress = " + freeMemory);
1515f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1525f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        logWriter.println
1535f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        ("--> Debuggee: createMemoryStress: OutOfMemoryError occured: " + countOOM);
1545f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    }
1555f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes*/
1565f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1575f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    /**
1585f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     * Custom class loader to be used for tested class.
1595f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     * It will be collected and finalized when tested class is unloaded.
1605f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes     */
1615f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    static class CustomLoader extends ClassLoader {
1625f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        private LogWriter logWriter;
1635f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1645f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        public CustomLoader(LogWriter writer) {
1655f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            this.logWriter = writer;
1665f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
1675f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
168e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz        @Override
1695f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        public Class<?> loadClass(String name) throws ClassNotFoundException {
1705f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            if (TESTED_CLASS_NAME.equals(name)) {
1715f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                // load only tested class with this loader
1725f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                return findClass(name);
1735f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            }
1745f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            return getParent().loadClass(name);
1755f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
1765f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
177e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz        @Override
1785f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        public Class<?> findClass(String name) throws ClassNotFoundException {
1795f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            try {
1805f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                logWriter.println("-->> CustomClassLoader: Find class: " + name);
1815f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	        	String res = name.replace('.', '/') + ".class";
1825f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	            URL url = getResource(res);
1835f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                logWriter.println("-->> CustomClassLoader: Found class file: " + res);
1845f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	    		InputStream is = url.openStream();
1855f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	            int size = 1024;
1865f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	            byte bytes[] = new byte[size];
1875f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	            int len = loadClassData(is, bytes, size);
1885f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                logWriter.println("-->> CustomClassLoader: Loaded class bytes: " + len);
189e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz	            Class<?> cls = defineClass(name, bytes, 0, len);
1905f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                logWriter.println("-->> CustomClassLoader: Defined class: " + cls);
1915f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes//	            resolveClass(cls);
1925f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes//                logWriter.println("-->> CustomClassLoader: Resolved class: " + cls);
1935f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes	            return cls;
1945f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        	} catch (Exception e) {
1955f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        		throw new ClassNotFoundException("Cannot load class: " + name, e);
1965f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        	}
1975f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
1985f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
1995f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        private int loadClassData(InputStream in, byte[] raw, int size) throws IOException {
2005f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            int len = in.read(raw);
2015f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            if (len >= size)
2025f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes                throw new IOException("Class file is too big: " + len);
2035f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            in.close();
2045f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            return len;
2055f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        }
2065f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
207e1833aa2673d4f975ad35e7a39317feeea922fe1Sebastien Hertz        @Override
2085f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes        protected void finalize() throws Throwable {
2095f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            logWriter.println("-->> CustomClassLoader: Class loader finalized => tested class UNLOADED");
2105f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes            ClassUnloadDebuggee.classUnloaded = true;
2115f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes       }
2125f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes    }
2135f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes}
2145f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes
2155f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes/**
2165f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes * Internal class used in ClassUnloadTest
2175f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes */
2185f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughesclass ClassUnloadTestedClass {
2195f0a23683aa603d8c50b6dd071a565821b76067bElliott Hughes}
220