1e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson/* 2e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Copyright (c) 2007 Mockito contributors 3e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * This program is made available under the terms of the MIT License. 4e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 5e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonpackage org.mockito.internal.configuration; 6e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 7e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.configuration.IMockitoConfiguration; 8e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.exceptions.base.MockitoException; 9e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.exceptions.misusing.MockitoConfigurationException; 10e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.plugins.MockMaker; 11e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport org.mockito.plugins.StackTraceCleanerProvider; 12e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 13e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.io.*; 14e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.net.URL; 15e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.util.ArrayList; 16e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.util.Collections; 17e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.util.Enumeration; 18e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonimport java.util.List; 19e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 20e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson/** 21e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Loads configuration or extension points available in the classpath. 22e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * 23e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <p> 24e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <ul> 25e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <li> 26e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Can load the mockito configuration. The user who want to provide his own mockito configuration 27e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * should write the class <code>org.mockito.configuration.MockitoConfiguration</code> that implements 28e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * {@link IMockitoConfiguration}. For example : 29e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <pre class="code"><code class="java"> 30e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * package org.mockito.configuration; 31e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * 32e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * //... 33e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * 34e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * public class MockitoConfiguration implements IMockitoConfiguration { 35e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * boolean enableClassCache() { return false; } 36e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * 37e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * // ... 38e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * } 39e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </code></pre> 40e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </li> 41e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <li> 42e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Can load available mockito extensions. Currently Mockito only have one extension point the 43e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * {@link MockMaker}. This extension point allows a user to provide his own bytecode engine to build mocks. 44e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <br>Suppose you wrote an extension to create mocks with some <em>Awesome</em> library, in order to tell 45e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Mockito to use it you need to put in your classpath 46e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <ol style="list-style-type: lower-alpha"> 47e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <li>The implementation itself, for example <code>org.awesome.mockito.AwesomeMockMaker</code>.</li> 48e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <li>A file named <code>org.mockito.plugins.MockMaker</code> in a folder named 49e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <code>mockito-extensions</code>, the content of this file need to have <strong>one</strong> line with 50e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * the qualified name <code>org.awesome.mockito.AwesomeMockMaker</code>.</li> 51e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </ol> 52e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </li> 53e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </ul> 54e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * </p> 55e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 56e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinsonpublic class ClassPathLoader { 57e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson private static final String DEFAULT_MOCK_MAKER_CLASS = 58e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson "org.mockito.internal.creation.CglibMockMaker"; 59e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson private static final String DEFAULT_STACK_TRACE_CLEANER_PROVIDER_CLASS = 60e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson "org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider"; 61e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson public static final String MOCKITO_CONFIGURATION_CLASS_NAME = "org.mockito.configuration.MockitoConfiguration"; 62e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 63e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson private static final MockMaker mockMaker = findPlatformMockMaker(); 64e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson private static final StackTraceCleanerProvider stackTraceCleanerProvider = 65e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson findPlatformStackTraceCleanerProvider(); 66e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 67e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson /** 68e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * @return configuration loaded from classpath or null 69e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 70e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson @SuppressWarnings({"unchecked"}) 71e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson public IMockitoConfiguration loadConfiguration() { 72e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson //Trying to get config from classpath 73e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson Class configClass; 74e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 75e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson configClass = (Class) Class.forName(MOCKITO_CONFIGURATION_CLASS_NAME); 76e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (ClassNotFoundException e) { 77e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson //that's ok, it means there is no global config, using default one. 78e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return null; 79e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 80e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 81e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 82e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return (IMockitoConfiguration) configClass.newInstance(); 83e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (ClassCastException e) { 84e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson throw new MockitoConfigurationException("MockitoConfiguration class must implement " + IMockitoConfiguration.class.getName() + " interface.", e); 85e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (Exception e) { 86e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson throw new MockitoConfigurationException("Unable to instantiate " + MOCKITO_CONFIGURATION_CLASS_NAME +" class. Does it have a safe, no-arg constructor?", e); 87e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 88e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 89e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 90e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson /** 91e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Returns the implementation of the mock maker available for the current runtime. 92e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * 93e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * <p>Returns {@link org.mockito.internal.creation.CglibMockMaker} if no 94e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * {@link MockMaker} extension exists or is visible in the current classpath.</p> 95e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 96e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson public static MockMaker getMockMaker() { 97e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return mockMaker; 98e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 99e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 100e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson public static StackTraceCleanerProvider getStackTraceCleanerProvider() { 101e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson //TODO we should throw some sensible exception if this is null. 102e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return stackTraceCleanerProvider; 103e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 104e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 105e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson /** 106e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Scans the classpath to find a mock maker plugin if one is available, 107e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * allowing mockito to run on alternative platforms like Android. 108e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 109e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static MockMaker findPlatformMockMaker() { 110e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return findPluginImplementation(MockMaker.class, DEFAULT_MOCK_MAKER_CLASS); 111e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 112e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 113e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static StackTraceCleanerProvider findPlatformStackTraceCleanerProvider() { 114e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return findPluginImplementation( 115e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson StackTraceCleanerProvider.class, DEFAULT_STACK_TRACE_CLEANER_PROVIDER_CLASS); 116e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 117e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 118e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static <T> T findPluginImplementation(Class<T> pluginType, String defaultPluginClassName) { 119e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson for (T plugin : loadImplementations(pluginType)) { 120e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return plugin; // return the first one service loader finds (if any) 121e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 122e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 123e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 124e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson // Default implementation. Use our own ClassLoader instead of the context 125e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson // ClassLoader, as the default implementation is assumed to be part of 126e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson // Mockito and may not be available via the context ClassLoader. 127e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return pluginType.cast(Class.forName(defaultPluginClassName).newInstance()); 128e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (Exception e) { 129e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson throw new MockitoException("Internal problem occurred, please report it. " + 130e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson "Mockito is unable to load the default implementation of class that is a part of Mockito distribution. " + 131e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson "Failed to load " + pluginType, e); 132e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 133e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 134e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 135e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson /** 136e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Equivalent to {@link java.util.ServiceLoader#load} but without requiring 137e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson * Java 6 / Android 2.3 (Gingerbread). 138e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson */ 139e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static <T> List<T> loadImplementations(Class<T> service) { 140e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson ClassLoader loader = Thread.currentThread().getContextClassLoader(); 141e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson if (loader == null) { 142e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson loader = ClassLoader.getSystemClassLoader(); 143e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 144e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson Enumeration<URL> resources; 145e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 146e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson resources = loader.getResources("mockito-extensions/" + service.getName()); 147e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (IOException e) { 148e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson throw new MockitoException("Failed to load " + service, e); 149e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 150e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 151e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson List<T> result = new ArrayList<T>(); 152e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson for (URL resource : Collections.list(resources)) { 153e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson InputStream in = null; 154e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 155e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson in = resource.openStream(); 156e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson for (String line : readerToLines(new InputStreamReader(in, "UTF-8"))) { 157e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson String name = stripCommentAndWhitespace(line); 158e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson if (name.length() != 0) { 159e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson result.add(service.cast(loader.loadClass(name).newInstance())); 160e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 161e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 162e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (Exception e) { 163e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson throw new MockitoConfigurationException( 164e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson "Failed to load " + service + " using " + resource, e); 165e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } finally { 166e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson closeQuietly(in); 167e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 168e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 169e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return result; 170e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 171e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 172e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static List<String> readerToLines(Reader reader) throws IOException { 173e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson List<String> result = new ArrayList<String>(); 174e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson BufferedReader lineReader = new BufferedReader(reader); 175e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson String line; 176e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson while ((line = lineReader.readLine()) != null) { 177e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson result.add(line); 178e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 179e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return result; 180e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 181e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 182e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson static String stripCommentAndWhitespace(String line) { 183e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson int hash = line.indexOf('#'); 184e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson if (hash != -1) { 185e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson line = line.substring(0, hash); 186e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 187e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson return line.trim(); 188e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 189e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson 190e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson private static void closeQuietly(InputStream in) { 191e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson if (in != null) { 192e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson try { 193e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson in.close(); 194e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } catch (IOException ignored) { 195e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 196e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 197e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson } 198e0ae5d7e87b1dd6e789803c1b9615a84bd7488b7Ian Parkinson}