1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.chain.web.jakarta;
18
19 import java.net.URL;
20 import java.util.ArrayList;
21 import java.util.List;
22
23 import org.apache.commons.chain.Catalog;
24 import org.apache.commons.chain.web.CheckedConsumer;
25 import org.apache.commons.chain.web.CheckedFunction;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import jakarta.servlet.ServletContext;
30
31 /**
32 * Utility methods for loading class loader and web application resources
33 * to configure a {@link Catalog}. These methods are shared between
34 * {@code ChainListener} and {@code ChainServlet}.
35 *
36 * @author Craig R. McClanahan
37 * @author Ted Husted
38 */
39 final class ChainResources {
40
41 /**
42 * This class uses a private constructor because it is a utility class.
43 */
44 private ChainResources() {
45 }
46
47 // ---------------------------------------------------------- Static Methods
48
49 /**
50 * Parse the specified class loader resources.
51 *
52 * @param <E> the type of the exception from parse-function
53 * @param resources Comma-delimited list of resources (or {@code null})
54 * @param parse parse-function to parse the XML document
55 */
56 static <E extends Exception> void parseClassResources(String resources,
57 CheckedConsumer<URL, E> parse) {
58
59 ClassLoader loader =
60 Thread.currentThread().getContextClassLoader();
61 if (loader == null) {
62 loader = ChainResources.class.getClassLoader();
63 }
64 parseResources(loader::getResource, resources, parse);
65 }
66
67 /**
68 * Parse the specified web application resources.
69 *
70 * @param <E> the type of the exception from parse-function
71 * @param context {@code ServletContext} for this web application
72 * @param resources Comma-delimited list of resources (or {@code null})
73 * @param parse parse-function to parse the XML document
74 */
75 static <E extends Exception> void parseWebResources(ServletContext context,
76 String resources,
77 CheckedConsumer<URL, E> parse) {
78
79 parseResources(context::getResource, resources, parse);
80 }
81
82 /**
83 * Parse the specified resources with a resource-get-function.
84 *
85 * @param <ER> the type of the exception from resource-function
86 * @param <EP> the type of the exception from parse-function
87 * @param resourceFunction function to get the {@link URL} from a path
88 * @param resources Comma-delimited list of resources (or {@code null})
89 * @param parse parse-function to parse the XML document
90 */
91 private static <ER extends Exception, EP extends Exception> void parseResources(
92 CheckedFunction<String, URL, ER> resourceFunction, String resources,
93 CheckedConsumer<URL, EP> parse) {
94
95 if (resources == null) {
96 return;
97 }
98 Logger logger = LoggerFactory.getLogger(ChainResources.class);
99 String[] paths = getResourcePaths(resources);
100 String path = null;
101 try {
102 for (String path2 : paths) {
103 path = path2;
104 URL url = resourceFunction.apply(path);
105 if (url == null) {
106 throw new IllegalStateException("Missing chain config resource '" + path + "'");
107 }
108 logger.debug("Loading chain config resource '{}'", path);
109 parse.accept(url);
110 }
111 } catch (Exception e) {
112 throw new RuntimeException("Exception parsing chain config resource '" + path + "': "
113 + e.getMessage());
114 }
115 }
116
117 /**
118 * Parse the resource string into an array of paths. Empty entries will
119 * be skipped. (That is, all entries in the array are non-empty paths.)
120 *
121 * @param resources A comma-delimited list of resource paths (or
122 * {@code null}).
123 *
124 * @return An array of non-empty paths. The array itself may be empty.
125 *
126 * @since Chain 1.1
127 */
128 static String[] getResourcePaths(String resources) {
129 final List<String> paths = new ArrayList<>();
130
131 if (resources != null) {
132 String path;
133 int comma;
134
135 int lastComma = 0;
136 while ((comma = resources.indexOf(',', lastComma)) >= 0) {
137 path = resources.substring(lastComma, comma).trim();
138 if (path.length() > 0) {
139 paths.add(path);
140 }
141 lastComma = comma + 1;
142 }
143 path = resources.substring(lastComma).trim();
144 if (path.length() > 0) {
145 paths.add(path);
146 }
147 }
148
149 return paths.toArray(new String[0]);
150 }
151 }