/*
* Copyright (C) 2014 Red Hat, inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.jboss.as.jaxrs;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.wildfly.extension.undertow.DeploymentDefinition.CONTEXT_ROOT;
import static org.wildfly.extension.undertow.DeploymentDefinition.SERVER;
import static org.wildfly.extension.undertow.DeploymentDefinition.VIRTUAL_HOST;
import io.undertow.servlet.api.ThreadSetupAction.Handle;
import io.undertow.servlet.handlers.ServletHandler;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleListAttributeDefinition;
import org.jboss.as.controller.SimpleOperationDefinition;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.msc.service.ServiceController;
import org.jboss.resteasy.core.ResourceInvoker;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ResourceMethodRegistry;
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
import org.wildfly.extension.undertow.UndertowExtension;
import org.wildfly.extension.undertow.UndertowService;
import org.wildfly.extension.undertow.deployment.UndertowDeploymentService;
/**
*
* @author <a href="mailto:ehugonne@redhat.com">Emmanuel Hugonnet</a> (c) 2014 Red Hat, inc.
*/
public class JaxrsDeploymentDefinition extends SimpleResourceDefinition {
public static final JaxrsDeploymentDefinition INSTANCE = new JaxrsDeploymentDefinition();
public static final String SHOW_RESOURCES = "show-resources";
public static final AttributeDefinition CLASSNAME
= new SimpleAttributeDefinitionBuilder("resource-class", ModelType.STRING, true).setStorageRuntime().build();
public static final AttributeDefinition PATH
= new SimpleAttributeDefinitionBuilder("resource-path", ModelType.STRING, true).setStorageRuntime().build();
public static final AttributeDefinition METHOD
= new SimpleAttributeDefinitionBuilder("jaxrs-resource-method", ModelType.STRING, false).setStorageRuntime().build();
public static final AttributeDefinition METHODS
= new SimpleListAttributeDefinition.Builder("resource-methods", METHOD).setStorageRuntime().build();
public static final ObjectTypeAttributeDefinition JAXRS_RESOURCE
= new ObjectTypeAttributeDefinition.Builder("jaxrs-resource", CLASSNAME, PATH, METHODS).setStorageRuntime().build();
private JaxrsDeploymentDefinition() {
super(JaxrsExtension.SUBSYSTEM_PATH, JaxrsExtension.getResolver());
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(ShowJaxrsResourcesHandler.DEFINITION, new ShowJaxrsResourcesHandler());
}
private static class ShowJaxrsResourcesHandler implements OperationStepHandler {
public static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(SHOW_RESOURCES,
JaxrsExtension.getResolver("deployment"))
.setReadOnly()
.setRuntimeOnly()
.setReplyType(ModelType.LIST)
.setDeprecated(JaxrsExtension.MODEL_VERSION_1_1_0)
.setReplyParameters(JAXRS_RESOURCE).build();
void handle(ModelNode response, String contextRootPath, Collection<String> servletMappings, String mapping, List<ResourceInvoker> resources) {
for (ResourceInvoker resourceInvoker : resources) {
if (ResourceMethodInvoker.class.isAssignableFrom(resourceInvoker.getClass())) {
ResourceMethodInvoker resource = (ResourceMethodInvoker) resourceInvoker;
final ModelNode node = new ModelNode();
node.get(CLASSNAME.getName()).set(resource.getResourceClass().getCanonicalName());
node.get(PATH.getName()).set(mapping);
for (String servletMapping : servletMappings) {
String method = formatMethod(resource, servletMapping, mapping, contextRootPath);
for (final String httpMethod : resource.getHttpMethods()) {
node.get(METHODS.getName()).add(String.format(method, httpMethod));
}
}
response.add(node);
}
}
}
private String formatMethod(ResourceMethodInvoker resource, String servletMapping, String path, String contextRootPath) {
StringBuilder builder = new StringBuilder();
builder.append("%1$s ");
String servletPath = servletMapping.replaceAll("\\*", "");
if(servletPath.charAt(0) == '/') {
servletPath = servletPath.substring(1);
}
builder.append(contextRootPath).append('/').append(servletPath).append(path);
builder.append(" - ").append(resource.getResourceClass().getCanonicalName()).append('.').append(resource.getMethod().getName()).append('(');
if (resource.getMethod().getParameterTypes().length > 0) {
builder.append("...");
}
builder.append(')');
return builder.toString().replaceAll("//", "/");
}
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
final PathAddress address = PathAddress.pathAddress(operation.get(ModelDescriptionConstants.OP_ADDR));
//Getting Undertow deployment Model to access Servlet informations.
final ModelNode subModel = context.readResourceFromRoot(address.subAddress(0, address.size() - 1).append(
SUBSYSTEM, UndertowExtension.SUBSYSTEM_NAME), false).getModel();
final String host = VIRTUAL_HOST.resolveModelAttribute(context, subModel).asString();
final String contextPath = CONTEXT_ROOT.resolveModelAttribute(context, subModel).asString();
final String server = SERVER.resolveModelAttribute(context, subModel).asString();
final ServiceController<?> controller = context.getServiceRegistry(false).getService(UndertowService.deploymentServiceName(server, host, contextPath));
final UndertowDeploymentService deploymentService = (UndertowDeploymentService) controller.getService();
Servlet resteasyServlet = null;
Handle handle = deploymentService.getDeployment().getThreadSetupAction().setup(null);
try {
for (Map.Entry<String, ServletHandler> servletHandler : deploymentService.getDeployment().getServlets().getServletHandlers().entrySet()) {
if (HttpServletDispatcher.class.isAssignableFrom(servletHandler.getValue().getManagedServlet().getServletInfo().getServletClass())) {
resteasyServlet = (Servlet) servletHandler.getValue().getManagedServlet().getServlet().getInstance();
break;
}
}
if (resteasyServlet != null) {
final Collection<String> servletMappings = resteasyServlet.getServletConfig().getServletContext().getServletRegistration(resteasyServlet.getServletConfig().getServletName()).getMappings();
final ResourceMethodRegistry registry = (ResourceMethodRegistry) ((HttpServletDispatcher) resteasyServlet).getDispatcher().getRegistry();
context.addStep(new OperationStepHandler() {
@Override
public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
if (registry != null) {
final ModelNode response = new ModelNode();
for (Map.Entry<String, List<ResourceInvoker>> resource : registry.getBounded().entrySet()) {
handle(response, contextPath, servletMappings, resource.getKey(), resource.getValue());
}
context.getResult().set(response);
}
}
}, OperationContext.Stage.RUNTIME);
}
} catch (ServletException ex) {
throw new RuntimeException(ex);
} finally {
handle.tearDown();
}
}
}
}