Skip to content

Commit 1389a55

Browse files
committed
Add Types.location which throws an exception
Like Types.load, this provides another signature with a "quietly" flag for toggling the exception throwing behavior. To test it, we load a class from a byte array, to ensure there is no discernable location.
1 parent fdc1335 commit 1389a55

File tree

2 files changed

+84
-11
lines changed

2 files changed

+84
-11
lines changed

src/main/java/org/scijava/util/Types.java

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import java.lang.reflect.WildcardType;
5959
import java.net.MalformedURLException;
6060
import java.net.URL;
61+
import java.security.CodeSource;
6162
import java.util.ArrayList;
6263
import java.util.Arrays;
6364
import java.util.Collections;
@@ -218,6 +219,18 @@ public static Class<?> load(final String name, final ClassLoader classLoader,
218219
}
219220
}
220221

222+
/**
223+
* Gets the base location of the given class.
224+
*
225+
* @param c The class whose location is desired.
226+
* @return URL pointing to the class, or null if the location could not be
227+
* determined.
228+
* @see #location(Class, boolean)
229+
*/
230+
public static URL location(final Class<?> c) {
231+
return location(c, true);
232+
}
233+
221234
/**
222235
* Gets the base location of the given class.
223236
* <p>
@@ -232,22 +245,33 @@ public static Class<?> load(final String name, final ClassLoader classLoader,
232245
* </p>
233246
*
234247
* @param c The class whose location is desired.
248+
* @param quietly Whether to return {@code null} (rather than throwing
249+
* {@link IllegalArgumentException}) if something goes wrong
250+
* determining the location.
235251
* @return URL pointing to the class, or null if the location could not be
236-
* determined.
252+
* determined and the {@code quietly} flag is set.
253+
* @throws IllegalArgumentException If the location cannot be determined and
254+
* the {@code quietly} flag is not set.
237255
* @see FileUtils#urlToFile(URL) to convert the result to a {@link File}.
238256
*/
239-
public static URL location(final Class<?> c) {
257+
public static URL location(final Class<?> c, final boolean quietly) {
258+
Exception cause = null;
259+
String why = null;
260+
240261
// try the easy way first
241262
try {
242-
final URL codeSourceLocation =
243-
c.getProtectionDomain().getCodeSource().getLocation();
244-
if (codeSourceLocation != null) return codeSourceLocation;
263+
final CodeSource codeSource = c.getProtectionDomain().getCodeSource();
264+
if (codeSource != null) {
265+
final URL location = codeSource.getLocation();
266+
if (location != null) return location;
267+
why = "null code source location";
268+
}
269+
else why = "null code source";
245270
}
246271
catch (final SecurityException exc) {
247272
// NB: Cannot access protection domain.
248-
}
249-
catch (final NullPointerException exc) {
250-
// NB: Protection domain or code source is null.
273+
cause = exc;
274+
why = "cannot access protection domain";
251275
}
252276

253277
// NB: The easy way failed, so we try the hard way. We ask for the class
@@ -256,11 +280,19 @@ public static URL location(final Class<?> c) {
256280

257281
// get the class's raw resource path
258282
final URL classResource = c.getResource(c.getSimpleName() + ".class");
259-
if (classResource == null) return null; // cannot find class resource
283+
if (classResource == null) {
284+
// cannot find class resource
285+
if (quietly) return null;
286+
throw iae(cause, "No class resource for class: " + name(c), why);
287+
}
260288

261289
final String url = classResource.toString();
262290
final String suffix = c.getCanonicalName().replace('.', '/') + ".class";
263-
if (!url.endsWith(suffix)) return null; // weird URL
291+
if (!url.endsWith(suffix)) {
292+
// weird URL
293+
if (quietly) return null;
294+
throw iae(cause, "Unsupported URL format: " + url, why);
295+
}
264296

265297
// strip the class's path from the URL string
266298
final String base = url.substring(0, url.length() - suffix.length());
@@ -274,7 +306,8 @@ public static URL location(final Class<?> c) {
274306
return new URL(path);
275307
}
276308
catch (final MalformedURLException e) {
277-
return null;
309+
if (quietly) return null;
310+
throw iae(e, "Malformed URL", why);
278311
}
279312
}
280313

src/test/java/org/scijava/util/TypesTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,22 @@ public void testLocationClassInJar() throws IOException {
185185
jar.deleteOnExit();
186186
}
187187

188+
/** Tests quiet behavior of {@link Types#location(Class, boolean)}. */
189+
@Test
190+
public void testLocationFailureQuiet() {
191+
final Class<?> weirdClass = loadCustomClass();
192+
assertEquals("Hello", weirdClass.getName());
193+
assertNull(Types.location(weirdClass));
194+
}
195+
196+
/** Tests exceptions from {@link Types#location(Class, boolean)}. */
197+
@Test(expected = IllegalArgumentException.class)
198+
public void testLocationFailureLoud() {
199+
final Class<?> weirdClass = loadCustomClass();
200+
assertEquals("Hello", weirdClass.getName());
201+
Types.location(weirdClass, false);
202+
}
203+
188204
/** Tests {@link Types#name}. */
189205
public void testName() {
190206
@SuppressWarnings("unused")
@@ -582,6 +598,30 @@ private void copy(final InputStream in, final OutputStream out,
582598
if (closeOut) out.close();
583599
}
584600

601+
private Class<?> loadCustomClass() {
602+
// NB: The bytecode below was compiled from the following source:
603+
//
604+
// public class Hello {}
605+
//
606+
final byte[] bytecode = { -54, -2, -70, -66, 0, 0, 0, 52, 0, 13, 10, 0, 3,
607+
0, 10, 7, 0, 11, 7, 0, 12, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3,
608+
40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78,
609+
117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 10, 83, 111, 117, 114,
610+
99, 101, 70, 105, 108, 101, 1, 0, 10, 72, 101, 108, 108, 111, 46, 106, 97,
611+
118, 97, 12, 0, 4, 0, 5, 1, 0, 5, 72, 101, 108, 108, 111, 1, 0, 16, 106,
612+
97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33,
613+
0, 2, 0, 3, 0, 0, 0, 0, 0, 1, 0, 1, 0, 4, 0, 5, 0, 1, 0, 6, 0, 0, 0, 29,
614+
0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 7, 0, 0, 0, 6,
615+
0, 1, 0, 0, 0, 1, 0, 1, 0, 8, 0, 0, 0, 2, 0, 9 };
616+
617+
class BytesClassLoader extends ClassLoader {
618+
public Class<?> load(final String name, final byte[] b) {
619+
return defineClass(name, b, 0, b.length);
620+
}
621+
}
622+
return new BytesClassLoader().load("Hello", bytecode);
623+
}
624+
585625
/** Convenience method to get the {@link Type} of a field. */
586626
private Type type(final Class<?> c, final String fieldName) {
587627
return Types.field(c, fieldName).getGenericType();

0 commit comments

Comments
 (0)