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}