1 /*
2 * Copyright 2016 Function1. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package tools.gsf.config;
17
18 import org.apache.commons.lang3.StringUtils;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import java.io.BufferedReader;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.net.URL;
29 import java.util.Enumeration;
30 import java.util.HashSet;
31 import java.util.Set;
32
33 /**
34 * Convenience class for working with reflection. Intended for internal GSF use only.
35 *
36 * @author Tony Field
37 * @since 2016-08-06
38 */
39 final class ReflectionUtils {
40
41 private static final Logger LOG = LoggerFactory.getLogger(ReflectionUtils.class);
42
43 private ReflectionUtils() {
44 }
45
46 /**
47 * Locate a single resource in the classpath of the classloader specified. If more than one matching
48 * resource is found, an IllegalStateException is thrown
49 *
50 * @param classLoader the classloader to search
51 * @param resourceName the name of the resource to find
52 * @return a URL to the resource
53 */
54 static URL getSingleResource(ClassLoader classLoader, String resourceName) {
55 try {
56 Enumeration<URL> resources = classLoader.getResources(resourceName);
57 URL result = null;
58 boolean bFound = false;
59 while (resources.hasMoreElements()) {
60 if (bFound) {
61 throw new IllegalStateException("Too many resources found matching name: " + resourceName);
62 }
63 result = resources.nextElement();
64 bFound = true;
65 }
66 return result;
67 } catch (IOException e) {
68 throw new IllegalStateException("Failed to locate resource: " + resourceName, e);
69 }
70 }
71
72 /**
73 * Locate a resource or resources in the classpath of the specified classloader, and read them.
74 * <p>
75 * More than one resource of the name specified is allowed.
76 * <p>
77 * The configuration resource file format ignores blank lines and lines starting with a #.
78 * <p>
79 * Each line of the configuration file will have its spaces normalized using
80 * org.apache.commons.lang3.StringUtils.normalizeSpace(String)
81 * <p>
82 * The configuration file names will be added to a set. If two matching configuration lines are
83 * found (after normalization), and IllegalStateException will be thrown.
84 *
85 * @param classLoader the classloader to search
86 * @param resourceName the name of the resource to find
87 * @return a set of the strings from the configuration resources. If no matching resources are
88 * found, or if the configuration resources are empty, an empty set will be returned.
89 * @throws IllegalStateException if more than one matching configuration line is found
90 * @throws RuntimeException if an error occurs reading the resources
91 */
92 static Set<String> readConfigurationResource(ClassLoader classLoader, String resourceName) {
93 Set<String> lines = new HashSet<>();
94 try {
95 Enumeration<URL> resources = classLoader.getResources(resourceName);
96 while (resources.hasMoreElements()) {
97 URL url = resources.nextElement();
98 try (InputStream in = url.openStream(); BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
99 String line;
100 while ((line = r.readLine()) != null) {
101 line = StringUtils.normalizeSpace(line);
102 if (StringUtils.isNotBlank(line) && !StringUtils.startsWith(line, "#")) {
103 if (lines.contains(line)) {
104 throw new IllegalStateException("Duplicate configuration information found in resource named " + resourceName + ". The following information was found more than once: " + line);
105 }
106 lines.add(line);
107 }
108 }
109 } catch (IOException e) {
110 throw new RuntimeException("Error reading configuration resource: " + resourceName, e);
111 }
112 }
113 } catch (IOException e) {
114 throw new RuntimeException("Error reading configuration resource: " + resourceName, e);
115 }
116 return lines;
117 }
118
119 /**
120 * @param <T> created method type
121 * @param name name of the object
122 * @param typeTocreate the type of the object to create
123 * @param factoryMethod the method to use to create the object
124 * @param factory The factory on which the factory method will be invoked
125 * @return created object
126 * @throws InvocationTargetException exception from invoking specified method from class name
127 */
128 @SuppressWarnings("unchecked")
129 static <T> T createFromMethod(String name, Class<T> typeTocreate, Object factory, Method factoryMethod, Object... params) throws InvocationTargetException {
130 Object o = null;
131 LOG.trace("Trying to create a {} object with name {} from method {} from factory {}", typeTocreate.getName(), name, factoryMethod.toGenericString(), factory.getClass().getName());
132
133 if (typeTocreate.isAssignableFrom(factoryMethod.getReturnType())) {
134 try {
135 o = factoryMethod.invoke(factory, params);
136 } catch (IllegalAccessException e) {
137 throw new IllegalStateException("Access exception creating object " + typeTocreate.getName() + ": " + e, e);
138 }
139 }
140 return (T) o;
141 }
142 }