-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
AdditionalServletUtils.java
162 lines (145 loc) · 6.97 KB
/
AdditionalServletUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.pulsar.broker.web.plugin.servlet;
import static com.google.common.base.Preconditions.checkArgument;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pulsar.common.nar.NarClassLoader;
import org.apache.pulsar.common.nar.NarClassLoaderBuilder;
import org.apache.pulsar.common.util.ObjectMapperFactory;
/**
* Util class to search and load {@link AdditionalServlets}.
*/
@UtilityClass
@Slf4j
public class AdditionalServletUtils {
public static final String ADDITIONAL_SERVLET_FILE = "additional_servlet.yml";
/**
* Retrieve the additional servlet definition from the provided nar package.
*
* @param narPath the path to the additional servlet NAR package
* @return the additional servlet definition
* @throws IOException when fail to load the additional servlet or get the definition
*/
public AdditionalServletDefinition getAdditionalServletDefinition(
String narPath, String narExtractionDirectory) throws IOException {
try (NarClassLoader ncl = NarClassLoaderBuilder.builder()
.narFile(new File(narPath))
.extractionDirectory(narExtractionDirectory)
.build();) {
return getAdditionalServletDefinition(ncl);
}
}
private AdditionalServletDefinition getAdditionalServletDefinition(NarClassLoader ncl) throws IOException {
String configStr = ncl.getServiceDefinition(ADDITIONAL_SERVLET_FILE);
return ObjectMapperFactory.getThreadLocalYaml().readValue(
configStr, AdditionalServletDefinition.class
);
}
/**
* Search and load the available additional servlets.
*
* @param additionalServletDirectory the directory where all the additional servlets are stored
* @return a collection of additional servlet definitions
* @throws IOException when fail to load the available additional servlets from the provided directory.
*/
public AdditionalServletDefinitions searchForServlets(String additionalServletDirectory,
String narExtractionDirectory) throws IOException {
Path path = Paths.get(additionalServletDirectory).toAbsolutePath();
log.info("Searching for additional servlets in {}", path);
AdditionalServletDefinitions servletDefinitions = new AdditionalServletDefinitions();
if (!path.toFile().exists()) {
log.warn("Pulsar additional servlets directory not found");
return servletDefinitions;
}
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.nar")) {
for (Path archive : stream) {
try {
AdditionalServletDefinition def =
AdditionalServletUtils.getAdditionalServletDefinition(
archive.toString(), narExtractionDirectory);
log.info("Found additional servlet from {} : {}", archive, def);
checkArgument(StringUtils.isNotBlank(def.getName()));
checkArgument(StringUtils.isNotBlank(def.getAdditionalServletClass()));
AdditionalServletMetadata metadata = new AdditionalServletMetadata();
metadata.setDefinition(def);
metadata.setArchivePath(archive);
servletDefinitions.servlets().put(def.getName(), metadata);
} catch (Throwable t) {
log.warn("Failed to load additional servlet from {}."
+ " It is OK however if you want to use this additional servlet,"
+ " please make sure you put the correct additional servlet NAR"
+ " package in the additional servlets directory.", archive, t);
}
}
}
return servletDefinitions;
}
/**
* Load the additional servlets according to the additional servlet definition.
*
* @param metadata the additional servlet definition.
*/
public AdditionalServletWithClassLoader load(
AdditionalServletMetadata metadata, String narExtractionDirectory) throws IOException {
final File narFile = metadata.getArchivePath().toAbsolutePath().toFile();
NarClassLoader ncl = NarClassLoaderBuilder.builder()
.narFile(narFile)
.parentClassLoader(AdditionalServlet.class.getClassLoader())
.extractionDirectory(narExtractionDirectory)
.build();
AdditionalServletDefinition def = getAdditionalServletDefinition(ncl);
if (StringUtils.isBlank(def.getAdditionalServletClass())) {
throw new IOException("Additional servlets `" + def.getName() + "` does NOT provide an "
+ "additional servlets implementation");
}
try {
Class additionalServletClass = ncl.loadClass(def.getAdditionalServletClass());
Object additionalServlet = additionalServletClass.getDeclaredConstructor().newInstance();
if (!(additionalServlet instanceof AdditionalServlet)) {
throw new IOException("Class " + def.getAdditionalServletClass()
+ " does not implement additional servlet interface");
}
AdditionalServlet servlet = (AdditionalServlet) additionalServlet;
return new AdditionalServletWithClassLoader(servlet, ncl);
} catch (Throwable t) {
rethrowIOException(t);
return null;
}
}
private void rethrowIOException(Throwable cause)
throws IOException {
if (cause instanceof IOException) {
throw (IOException) cause;
} else if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
} else {
throw new IOException(cause.getMessage(), cause);
}
}
}