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 package info.magnolia.rest;
35
36 import info.magnolia.config.registry.DefinitionProvider;
37 import info.magnolia.event.EventBus;
38 import info.magnolia.event.HandlerRegistration;
39 import info.magnolia.event.SystemEventBus;
40 import info.magnolia.objectfactory.ComponentProvider;
41 import info.magnolia.objectfactory.Components;
42 import info.magnolia.rest.provider.AdditionalProviderDefinition;
43 import info.magnolia.rest.registry.EndpointDefinitionRegistry;
44 import info.magnolia.rest.registry.EndpointDefinitionRegistryEvent;
45 import info.magnolia.rest.registry.EndpointDefinitionRegistryEventHandler;
46
47 import java.io.File;
48 import java.util.Arrays;
49 import java.util.HashSet;
50 import java.util.Map;
51 import java.util.Set;
52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.stream.Collectors;
54
55 import javax.inject.Inject;
56 import javax.inject.Named;
57 import javax.servlet.ServletConfig;
58 import javax.servlet.ServletException;
59 import javax.ws.rs.core.Application;
60
61 import org.apache.commons.lang3.StringUtils;
62 import org.jboss.resteasy.plugins.server.servlet.ConfigurationBootstrap;
63 import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
64 import org.jboss.resteasy.plugins.server.servlet.ServletBootstrap;
65 import org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher;
66 import org.jboss.resteasy.spi.ResteasyDeployment;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70
71
72
73 public class RestDispatcherServlet extends HttpServletDispatcher implements EndpointDefinitionRegistryEventHandler {
74
75 private static final Logger log = LoggerFactory.getLogger(RestDispatcherServlet.class);
76
77 private final RestIntegrationModule restIntegrationModule;
78 private final EndpointDefinitionRegistry endpointRegistry;
79 private final EventBus systemEventBus;
80 private final Map<String, Object> endpoints = new ConcurrentHashMap<>();
81 private final ComponentProvider componentProvider;
82
83 private Application application = new Application() {
84 @Override
85 public Set<Object> getSingletons() {
86 HashSet<Object> singletons = new HashSet<>();
87 singletons.addAll(endpoints.values());
88 return singletons;
89 }
90 };
91 private ServletConfig servletConfig;
92 private HandlerRegistration registerHandler;
93
94 @Inject
95 public RestDispatcherServlet(final RestIntegrationModule restIntegrationModule, EndpointDefinitionRegistry endpointRegistry, @Named(SystemEventBus.NAME) EventBus systemEventBus, ComponentProvider componentProvider) {
96 this.restIntegrationModule = restIntegrationModule;
97 this.endpointRegistry = endpointRegistry;
98 this.systemEventBus = systemEventBus;
99 this.componentProvider = componentProvider;
100 }
101
102 @Deprecated
103 public RestDispatcherServlet(final RestIntegrationModule restIntegrationModule, EndpointDefinitionRegistry endpointRegistry, @Named(SystemEventBus.NAME) EventBus systemEventBus) {
104 this(restIntegrationModule, endpointRegistry, systemEventBus, Components.getComponentProvider());
105 }
106
107 @Override
108 public void init(ServletConfig servletConfig) throws ServletException {
109 this.servletConfig = servletConfig;
110
111
112 servletContainerDispatcher = new ServletContainerDispatcher();
113 ConfigurationBootstrap bootstrap = createBootstrap(servletConfig);
114 servletContainerDispatcher.init(servletConfig.getServletContext(), bootstrap, this, this);
115 servletContainerDispatcher.getDispatcher().getDefaultContextObjects().put(ServletConfig.class, servletConfig);
116
117
118 for (AdditionalProviderDefinition provider : restIntegrationModule.getAdditionalProviders()) {
119 log.debug("Registering additional provider [{}]", provider.getProviderClass());
120 super.getDispatcher().getProviderFactory().registerProvider(provider.getProviderClass());
121 }
122
123
124 for (DefinitionProvider<EndpointDefinition> provider : endpointRegistry.getAllProviders()) {
125 try {
126 registerEndpoint(provider);
127 } catch (Exception e) {
128 log.error("Failed to register endpoint [{}]", provider.getMetadata().getReferenceId(), e);
129
130 }
131 }
132
133
134 registerHandler = systemEventBus.addHandler(EndpointDefinitionRegistryEvent.class, this);
135 }
136
137 @Override
138 public void destroy() {
139 registerHandler.removeHandler();
140 super.destroy();
141 endpoints.clear();
142 }
143
144
145
146 @Override
147 public void onEndpointRegistered(EndpointDefinitionRegistryEvent event) {
148 DefinitionProvider<EndpointDefinition> provider = event.getEndpointDefinitionProvider();
149 final String referenceId = provider.getMetadata().getReferenceId();
150 if (endpoints.containsKey(referenceId)) {
151 unregisterEndpoint(referenceId);
152 }
153 registerEndpoint(provider);
154 }
155
156 @Override
157 public void onEndpointReregistered(EndpointDefinitionRegistryEvent event) {
158 DefinitionProvider<EndpointDefinition> provider = event.getEndpointDefinitionProvider();
159 unregisterEndpoint(provider.getMetadata().getReferenceId());
160 registerEndpoint(provider);
161 }
162
163 @Override
164 public void onEndpointUnregistered(EndpointDefinitionRegistryEvent event) {
165 unregisterEndpoint(event.getEndpointName());
166 }
167
168
169 @Override
170 public ServletConfig getServletConfig() {
171 return servletConfig;
172 }
173
174
175
176
177
178 protected Object registerEndpoint(EndpointDefinition endpointDefinition) {
179 if (!endpointDefinition.isEnabled()) {
180 return null;
181 }
182 Object endpoint = instantiateEndpoint(endpointDefinition);
183 endpoints.put(endpointDefinition.getName(), endpoint);
184 super.getDispatcher().getRegistry().addSingletonResource(endpoint);
185 return endpoint;
186 }
187
188 protected Object registerEndpoint(DefinitionProvider<EndpointDefinition> provider) {
189 if (!provider.isValid()) {
190 return null;
191 }
192
193 EndpointDefinition endpointDefinition = provider.get();
194 String endpointReferenceId = provider.getMetadata().getReferenceId();
195
196 if (!endpointDefinition.isEnabled()) {
197 log.info("Endpoint {} is disabled, skipping registration", endpointReferenceId);
198 return null;
199 }
200
201 Object endpoint = instantiateEndpoint(endpointDefinition);
202
203 if (supportDynamicPath(endpointDefinition.getImplementationClass())) {
204 String path;
205 if (StringUtils.isNotEmpty(endpointDefinition.getEndpointPath())) {
206 path = truncatePath(endpointDefinition.getEndpointPath());
207 } else {
208 path = getBasePath(endpointReferenceId);
209 }
210 super.getDispatcher().getRegistry().addSingletonResource(endpoint, path);
211 log.info("Endpoint {} is registered with base path: [context]/{}", endpointReferenceId, path);
212 endpoints.put(endpointReferenceId, endpoint);
213 } else {
214 super.getDispatcher().getRegistry().addSingletonResource(endpoint);
215 endpoints.put(endpointReferenceId, endpoint);
216 }
217
218 return endpoint;
219 }
220
221 protected void unregisterEndpoint(String endpointReferenceId) {
222 Object endpoint = endpoints.remove(endpointReferenceId);
223 if (endpoint != null) {
224 Class<?> endpointClass = endpoint.getClass();
225 if (supportDynamicPath(endpointClass) && endpoint instanceof AbstractEndpoint) {
226 String configuredPath = ((AbstractEndpoint) endpoint).getEndpointDefinition().getEndpointPath();
227 String path = StringUtils.isEmpty(configuredPath) ? getBasePath(endpointReferenceId) : truncatePath(configuredPath);
228 super.getDispatcher().getRegistry().removeRegistrations(endpointClass, path);
229 log.debug("Unregister endpoint {} with base path {} from registry.", endpointReferenceId, path);
230 } else {
231 super.getDispatcher().getRegistry().removeRegistrations(endpointClass);
232 log.debug("Unregister endpoint which has reference id: {} from registry.", endpointReferenceId);
233 }
234
235 }
236 }
237
238 protected Object instantiateEndpoint(EndpointDefinition endpointDefinition) {
239 return componentProvider.newInstance(endpointDefinition.getImplementationClass(), endpointDefinition);
240 }
241
242
243
244 protected Application getApplication() {
245 return application;
246 }
247
248 protected ConfigurationBootstrap createBootstrap(ServletConfig servletConfig) {
249 return new ServletBootstrap(servletConfig) {
250 @Override
251 public ResteasyDeployment createDeployment() {
252 return configureDeployment(super.createDeployment());
253 }
254 };
255 }
256
257 protected ResteasyDeployment configureDeployment(ResteasyDeployment deployment) {
258 deployment.setApplicationClass(null);
259 deployment.setApplication(getApplication());
260 return deployment;
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274 protected String getBasePath(String endpointReferenceId) {
275 final String[] endpointPaths = StringUtils.split(endpointReferenceId, File.separator);
276 endpointPaths[endpointPaths.length - 1] = endpointPaths[endpointPaths.length - 1].replace('_', '/');
277 return Arrays.stream(endpointPaths).collect(Collectors.joining("/"));
278 }
279
280 private boolean supportDynamicPath(final Class<?> implementationClass) {
281 return implementationClass != null && implementationClass.getAnnotation(DynamicPath.class) != null;
282 }
283
284 private String truncatePath(String configuredPath) {
285 String path = StringUtils.removeEnd(configuredPath, "/");
286 path = StringUtils.removeStart(path, "/");
287 return path;
288 }
289 }