1

我正在准备一个 ReSTful 服务,我想用 RAML(也许还有 Swagger)来记录它,但似乎我不能同时在同一个应用程序中实现 JAX-RS 和 RAML。

我为 JAX-RS 创建了一个 Application 类,如下所示:

public class Application extends javax.ws.rs.core.Application {

    @Override
    public Set<Class<?>> getClasses() {
        // Use the reflections library to scan the current package tree for
        // classes annotated with javax.ws.rs.Path and add them to the JAX-RS
        // application
        Reflections reflections = new Reflections(this.getClass().getPackage().getName());
        return reflections.getTypesAnnotatedWith(Path.class);
    }
}

我按如下方式附加 JAX-RS 应用程序对象:

    Component component = new Component();
    Server server = new Server(Protocol.HTTP, PORT);
    component.getServers().add(server);

    JaxRsApplication jaxRsApplication = new JaxRsApplication(component.getContext().createChildContext());
    jaxRsApplication.add(new Application());
    jaxRsApplication.setObjectFactory(objectFactory);
    component.getDefaultHost().attach("/rest", jaxRsApplication);

而且我还想实现 RAML 扩展,但它看起来像绑定到 Restlet 路由器并拥有它自己的应用程序类。有没有办法将两者结合起来?

4

1 回答 1

1

Indeed the RAML extension of Restlet isn't designed to be used within JAXRS application. That said you can define a resource that provide the RAML content based on classes ApplicationIntrospector of Restlet and RamlEmitter of RAML parser, as described below:

public class RamlResource {
    private Definition definition;

    @Path("/raml")
    @GET
    public String getRaml() {
        return new RamlEmitter().dump(RamlTranslator
                         .getRaml(getDefinition()));
    }

    private synchronized Definition getDefinition() {
        if (definition == null) {
            synchronized (RamlResource.class) {
                definition = ApplicationIntrospector.getDefinition(
                               Application.getCurrent(),
                               new Reference("/"), null, false);
            }
        }

        return definition;
    }
}

It's the way the RAML extension of Restlet works. You could also use such an approach for Swagger but be careful since Swagger 1.2 requires several resources (a main and several sub ones with each categories). It's not the case anymore for Swagger 2.

You can notice that there is a JAX-RS support for Swagger in the extension org.restlet.ext.swagger.

----- Edited

Perhaps can you make a try with this class that corresponds to a port of the class JaxRsApplicationSwaggerSpecificationRestlet to RAML. It's based on the class JaxRsIntrospector which seems relevant for JAX-RS application:

public class JaxRsApplicationRamlSpecificationRestlet extends Restlet {
    private Application application;
    private String basePath;
    private Reference baseRef;
    private Definition definition;

    public JaxRsApplicationRamlSpecificationRestlet(Application application) {
        this(null, application);
    }

    public JaxRsApplicationRamlSpecificationRestlet(Context context, Application application) {
        super(context);
        this.application = application;
    }

    public void attach(Router router) {
        attach(router, "/api-docs");
    }

    public void attach(Router router, String path) {
        router.attach(path, this);
        router.attach(path + "/{resource}", this);
    }

    public Representation getApiDeclaration() {
        Raml raml = RamlTranslator.getRaml(
            getDefinition());
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());

        try {
            return new StringRepresentation(
                      mapper.writeValueAsString(raml), 
                      MediaType.APPLICATION_YAML);
        } catch (Exception ex) {
            return new StringRepresentation("error");
        }
    }

    public String getBasePath() {
        return basePath;
    }

    private synchronized Definition getDefinition() {
        if (definition == null) {
            synchronized (JaxRsApplicationRamlSpecificationRestlet.class) {
                definition = JaxRsIntrospector.getDefinition(application,
                    baseRef, false);
            }
        }

        return definition;
    }

    @Override
    public void handle(Request request, Response response) {
        super.handle(request, response);

        if (Method.GET.equals(request.getMethod())) {
            response.setEntity(getApiDeclaration());
        } else {
                response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
        }
    }

    public void setApiInboundRoot(Application application) {
        this.application = application;
    }

    public void setApplication(Application application) {
        this.application = application;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
        // Process basepath and check validity
        this.baseRef = basePath != null ? new Reference(basePath) :  null;
    }
}

You can use this class like this:

JaxRsApplication application
       = new JaxRsApplication(component.getContext());
MyApplication app = new MyApplication();
application.add(app);

new JaxRsApplicationRamlSpecificationRestlet(app);

(...)

There is no need for a dedicated resource. Please note that this code is a bit experimental ;-) I could propose it back for a contribution for the extension raml in Restlet...

Hope it helps you, Thierry

于 2015-04-13T09:48:20.580 回答