Java-Джерси - Как получить весь url, который разрабатывается в проекте веб-приложения maven [дубликат]

Другой вопрос был назван дубликатом этого:

В C ++ почему результат cout << x отличается от значения, которое показывает отладчик для x ?

x в вопросе - это переменная float.

Одним из примеров может быть

float x = 9.9F;

Отладчик показывает 9.89999962, вывод работы cout - 9.9.

Ответ оказывается, что точность cout по умолчанию для float равна 6, поэтому она округляется до шести десятичных цифры

См. здесь для справки

16
задан Paul Samsotha 6 February 2016 в 06:12
поделиться

4 ответа

Вероятно, лучший способ сделать это - использовать ApplicationEventListener . Оттуда вы можете прослушать событие «завершение инициализации приложения» и получить ResourceModel из ApplicationEvent. ResourceModel будет иметь все инициализированные Resource s. Затем вы можете пересечь Resource, как упомянули другие. Ниже приведена реализация. Некоторые из реализации были взяты из реализации DropwizardResourceConfig .

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.server.model.ResourceModel;
import org.glassfish.jersey.server.monitoring.ApplicationEvent;
import org.glassfish.jersey.server.monitoring.ApplicationEventListener;
import org.glassfish.jersey.server.monitoring.RequestEvent;
import org.glassfish.jersey.server.monitoring.RequestEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EndpointLoggingListener implements ApplicationEventListener {

    private static final TypeResolver TYPE_RESOLVER = new TypeResolver();

    private final String applicationPath;

    private boolean withOptions = false;
    private boolean withWadl = false;

    public EndpointLoggingListener(String applicationPath) {
        this.applicationPath = applicationPath;
    }

    @Override
    public void onEvent(ApplicationEvent event) {
        if (event.getType() == ApplicationEvent.Type.INITIALIZATION_APP_FINISHED) {
            final ResourceModel resourceModel = event.getResourceModel();
            final ResourceLogDetails logDetails = new ResourceLogDetails();
            resourceModel.getResources().stream().forEach((resource) -> {
                logDetails.addEndpointLogLines(getLinesFromResource(resource));
            });
            logDetails.log();
        }
    }

    @Override
    public RequestEventListener onRequest(RequestEvent requestEvent) {
        return null;
    }

    public EndpointLoggingListener withOptions() {
        this.withOptions = true;
        return this;
    }

    public EndpointLoggingListener withWadl() {
        this.withWadl = true;
        return this;
    }

    private Set<EndpointLogLine> getLinesFromResource(Resource resource) {
        Set<EndpointLogLine> logLines = new HashSet<>();
        populate(this.applicationPath, false, resource, logLines);
        return logLines;
    }

    private void populate(String basePath, Class<?> klass, boolean isLocator,
            Set<EndpointLogLine> endpointLogLines) {
        populate(basePath, isLocator, Resource.from(klass), endpointLogLines);
    }

    private void populate(String basePath, boolean isLocator, Resource resource,
            Set<EndpointLogLine> endpointLogLines) {
        if (!isLocator) {
            basePath = normalizePath(basePath, resource.getPath());
        }

        for (ResourceMethod method : resource.getResourceMethods()) {
            if (!withOptions && method.getHttpMethod().equalsIgnoreCase("OPTIONS")) {
                continue;
            }
            if (!withWadl && basePath.contains(".wadl")) {
                continue;
            }
            endpointLogLines.add(new EndpointLogLine(method.getHttpMethod(), basePath, null));
        }

        for (Resource childResource : resource.getChildResources()) {
            for (ResourceMethod method : childResource.getAllMethods()) {
                if (method.getType() == ResourceMethod.JaxrsType.RESOURCE_METHOD) {
                    final String path = normalizePath(basePath, childResource.getPath());
                    if (!withOptions && method.getHttpMethod().equalsIgnoreCase("OPTIONS")) {
                        continue;
                    }
                    if (!withWadl && path.contains(".wadl")) {
                        continue;
                    }
                    endpointLogLines.add(new EndpointLogLine(method.getHttpMethod(), path, null));
                } else if (method.getType() == ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR) {
                    final String path = normalizePath(basePath, childResource.getPath());
                    final ResolvedType responseType = TYPE_RESOLVER
                            .resolve(method.getInvocable().getResponseType());
                    final Class<?> erasedType = !responseType.getTypeBindings().isEmpty()
                            ? responseType.getTypeBindings().getBoundType(0).getErasedType()
                            : responseType.getErasedType();
                    populate(path, erasedType, true, endpointLogLines);
                }
            }
        }
    }

    private static String normalizePath(String basePath, String path) {
        if (path == null) {
            return basePath;
        }
        if (basePath.endsWith("/")) {
            return path.startsWith("/") ? basePath + path.substring(1) : basePath + path;
        }
        return path.startsWith("/") ? basePath + path : basePath + "/" + path;
    }

    private static class ResourceLogDetails {

        private static final Logger logger = LoggerFactory.getLogger(ResourceLogDetails.class);

        private static final Comparator<EndpointLogLine> COMPARATOR
                = Comparator.comparing((EndpointLogLine e) -> e.path)
                .thenComparing((EndpointLogLine e) -> e.httpMethod);

        private final Set<EndpointLogLine> logLines = new TreeSet<>(COMPARATOR);

        private void log() {
            StringBuilder sb = new StringBuilder("\nAll endpoints for Jersey application\n");
            logLines.stream().forEach((line) -> {
                sb.append(line).append("\n");
            });
            logger.info(sb.toString());
        }

        private void addEndpointLogLines(Set<EndpointLogLine> logLines) {
            this.logLines.addAll(logLines);
        }
    }

    private static class EndpointLogLine {

        private static final String DEFAULT_FORMAT = "   %-7s %s";
        final String httpMethod;
        final String path;
        final String format;

        private EndpointLogLine(String httpMethod, String path, String format) {
            this.httpMethod = httpMethod;
            this.path = path;
            this.format = format == null ? DEFAULT_FORMAT : format;
        }

        @Override
        public String toString() {
            return String.format(format, httpMethod, path);
        }
    }
}

Тогда вам просто нужно зарегистрировать слушателя с Джерси. Вы можете получить путь к программе из JerseyProperties. Вам нужно будет установить его в Spring Boot application.properties в свойстве spring.jersey.applicationPath. Это будет корневой путь, как если бы вы использовали @ApplicationPath в подклассе ResourceConfig

@Bean
public ResourceConfig getResourceConfig(JerseyProperties jerseyProperties) {
    return new JerseyConfig(jerseyProperties);
}
...
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig(JerseyProperties jerseyProperties) {
        register(HelloResource.class);
        register(new EndpointLoggingListener(jerseyProperties.getApplicationPath()));
    }
}

. Следует отметить, что загрузка при запуске не задается по умолчанию на сервлете Джерси. Это означает, что Джерси не загружается при запуске до первого запроса. Таким образом, вы не увидите слушателя, вызванного до первого запроса. Я открыл проблему , чтобы получить свойство конфигурации, но в то же время у вас есть несколько вариантов:

  1. Настройте Джерси как фильтр, а не сервлет , Фильтр будет загружен при запуске. Использование Джерси в качестве фильтра, для большинства сообщений, действительно не ведет себя иначе. Чтобы настроить это, вам просто нужно добавить свойство Spring Boot в application.properties
    spring.jersey.type=filter
    
  2. . Другой вариант - переопределить Джерси ServletRegistrationBean и установить его свойство loadOnStartup. Вот пример конфигурации. Некоторые из реализаций были взяты прямо из JerseyAutoConfiguration
    @SpringBootApplication
    public class JerseyApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(JerseyApplication.class, args);
        }
    
        @Bean
        public ResourceConfig getResourceConfig(JerseyProperties jerseyProperties) {
            return new JerseyConfig(jerseyProperties);
        }
    
        @Bean
        public ServletRegistrationBean jerseyServletRegistration(
            JerseyProperties jerseyProperties, ResourceConfig config) {
            ServletRegistrationBean registration = new ServletRegistrationBean(
                    new ServletContainer(config), 
                    parseApplicationPath(jerseyProperties.getApplicationPath())
            );
            addInitParameters(registration, jerseyProperties);
            registration.setName(JerseyConfig.class.getName());
            registration.setLoadOnStartup(1);
            return registration;
        }
    
        private static String parseApplicationPath(String applicationPath) {
            if (!applicationPath.startsWith("/")) {
                applicationPath = "/" + applicationPath;
            }
            return applicationPath.equals("/") ? "/*" : applicationPath + "/*";
        }
    
        private void addInitParameters(RegistrationBean registration, JerseyProperties jersey) {
            for (Entry<String, String> entry : jersey.getInit().entrySet()) {
                registration.addInitParameter(entry.getKey(), entry.getValue());
            }
        }
    }
    

UPDATE

Так выглядит Spring Boot добавит свойство load-on-startup , поэтому нам не нужно переопределять Jersey ServletRegistrationBean. Будет добавлен в Boot 1.4.0

11
ответ дан Paul Samsotha 5 September 2018 в 09:09
поделиться

Можете ли вы использовать ResourceConfig#getResources на вашем объекте ResourceConfig, а затем получить нужную информацию, итерации через Set<Resource> , который он возвращает?

Извиняюсь, попробовал бы это, но у меня нет ресурсов , чтобы сделать это прямо сейчас. :-p

1
ответ дан Carlos Bribiescas 5 September 2018 в 09:09
поделиться

После того, как приложение полностью запущено, вы можете спросить ServerConfig:

ResourceConfig instance; 
ServerConfig scfg = instance.getConfiguration();
Set<Class<?>> classes = scfg.getClasses();

classes содержит все кэшированные классы конечных точек.

Из API docs для javax.ws.rs.core.Configuration:

Получите неизменяемый набор зарегистрированных компонентов JAX-RS (таких как поставщик или объект), которые должны быть созданы, введены и использованы в рамках настраиваемый экземпляр.

Однако вы не можете сделать это в коде init вашего приложения, классы могут еще не полностью загрузиться.

С помощью классов вы можете сканировать их для ресурсов:

public Map<String, List<InfoLine>> scan(Class baseClass) {
    Builder builder = Resource.builder(baseClass);
    if (null == builder)
        return null;
    Resource resource = builder.build();
    String uriPrefix = "";
    Map<String, List<InfoLine>> info = new TreeMap<>();
    return process(uriPrefix, resource, info);
}

private Map<String, List<InfoLine>> process(String uriPrefix, Resource resource, Map<String, List<InfoLine>> info) {
    String pathPrefix = uriPrefix;
    List<Resource> resources = new ArrayList<>();
    resources.addAll(resource.getChildResources());
    if (resource.getPath() != null) {
        pathPrefix = pathPrefix + resource.getPath();
    }
    for (ResourceMethod method : resource.getAllMethods()) {
        if (method.getType().equals(ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR)) {
            resources.add(
                Resource.from(
                    resource.getResourceLocator()
                            .getInvocable()
                            .getDefinitionMethod()
                            .getReturnType()
                )
            );
        }
        else {
            List<InfoLine> paths = info.get(pathPrefix);
            if (null == paths) {
                paths = new ArrayList<>();
                info.put(pathPrefix, paths);
            }
            InfoLine line = new InfoLine();
            line.pathPrefix = pathPrefix;
            line.httpMethod = method.getHttpMethod();
            paths.add(line);
            System.out.println(method.getHttpMethod() + "\t" + pathPrefix);
        }
    }
    for (Resource childResource : resources) {
        process(pathPrefix, childResource, info);
    }
    return info;
}


private class InfoLine {
    public String pathPrefix;
    public String httpMethod;
}
1
ответ дан Johannes Jander 5 September 2018 в 09:09
поделиться

Как насчет использования RequestMappingHandlerMapping, который содержит всю информацию о конечных точках.

См. мой ответ в Как получить доступ ко всем доступным маршрутам API REST от контроллера? .

0
ответ дан Thomas Decaux 5 September 2018 в 09:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: