Могу ли я использовать расширения JAX-RS и RAML в Restlet в одном приложении?

Я готовлю службу ReSTful, которую я хотел бы задокументировать с использованием RAML (и, возможно, Swagger), но, похоже, я не могу одновременно реализовать JAX-RS и RAML в одном приложении.

Я создал класс приложения для JAX-RS следующим образом:

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 и имеет собственный класс приложения. Есть ли способ объединить их?


person Deven Phillips    schedule 04.04.2015    source источник


Ответы (1)


Действительно, расширение Restlet RAML не предназначено для использования в приложении JAXRS. Тем не менее, вы можете определить ресурс, который предоставляет контент RAML на основе классов ApplicationIntrospector Restlet и RamlEmitter парсера RAML, как описано ниже:

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;
    }
}

Так работает RAML-расширение Restlet. Вы также можете использовать такой подход для Swagger, но будьте осторожны, поскольку для Swagger 1.2 требуется несколько ресурсов (основной и несколько дополнительных для каждой категории). Это больше не относится к Swagger 2.

Вы можете заметить, что в расширении org.restlet.ext.swagger есть поддержка JAX-RS для Swagger.

----- Отредактировано

Возможно, вы можете попробовать этот класс, который соответствует порту класса JaxRsApplicationSwaggerSpecificationRestlet на RAML. Он основан на классе JaxRsIntrospector, который кажется актуальным для приложения JAX-RS:

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;
    }
}

Вы можете использовать этот класс следующим образом:

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

new JaxRsApplicationRamlSpecificationRestlet(app);

(...)

Нет необходимости в специальном ресурсе. Обратите внимание, что этот код немного экспериментальный ;-) Я мог бы предложить его обратно для вклада в расширение raml в Restlet...

Надеюсь, это поможет вам, Тьерри.

person Thierry Templier    schedule 13.04.2015
comment
Это выглядит многообещающе, но я не могу заставить его работать. Когда я попытался реализовать, как описано, я получаю исключение NullPointerException в ApplicationIntrospector.addEnpoints()... Результат baseRef.getSchemeProtocol() равен нулю. Я предполагаю, что это то, чего мне не хватало при первоначальной настройке приложения? - person Deven Phillips; 16.04.2015
comment
К вашему сведению, исходный код моего проекта можно найти по адресу: github.com/InfoSec812/restlet-deltaspike. -демо - person Deven Phillips; 16.04.2015
comment
Я добился большего прогресса, используя другой конструктор для «Ссылки». Проблема в том, что хотя исключения не выбрасываются, сгенерированный RAML в основном пуст. - person Deven Phillips; 16.04.2015
comment
Я отредактировал свой ответ, используя новый подход, аналогичный подходу из расширения swagger. С ним я более уверен ;-) Можешь попробовать? Спасибо! - person Thierry Templier; 16.04.2015
comment
Я попытался реализовать то, что вы описали выше, но я все еще получаю сообщение об ошибке на стороне сервера. Мне интересно, делаю ли я что-то другое во время инициализации, что вызывает проблему. Я отправил свои изменения в репозиторий GitHub, указанный выше, если вам интересно посмотреть. - person Deven Phillips; 20.04.2015