how to do friendly base url for swagger 2.8.0 - java

I'm trying to change base access url for API documentation. The url is "http://localhost:8080/swagger-ui.html". I want to get something like "http://localhost:8080/myapi/swagger-ui.html".
I use Springfox 2.8.0 Swagger, Java 8, Spring Boot 2.0
The swagger configuration is:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)
.pathProvider(new RelativePathProvider(servletContext) {
#Override
public String getApplicationBasePath() {
return "/myapi";
}
})
.select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error")))
.build()
.useDefaultResponseMessages(false);
}
}
Custom path provider had to help, but I still get access to api documentation by using url "http://localhost:8080/swagger-ui.html". If I use url "http://localhost:8080/myapi/swagger-ui.html", I get 404 error. Look at the screenshot below.

UPD: Springfox is abandoned
Springfox Swagger had always been kinda dirty solution with a lot of unclearness and bugs, but by now (2021 Q4) it hadn't been updated for more than a year.
The final straw was the fact that Springfox Swagger 3.0 doesn't work anymore with Spring Boot 2.6.x.
So, if you reading this, please, consider switching over to https://springdoc.org/ instead.
It's a pretty straightforward conversion and they do a great job of
documenting it. https://springdoc.org/#migrating-from-springfox.
For those who use Springfox Swagger 3.0.0
Here's the working configuration for changing base url for docs:
springfox:
documentation:
swaggerUi:
baseUrl: /documentation
openApi:
v3:
path: /documentation/v3/api-docs
swagger:
v2:
path: /documentation/v2/api-docs

You can edit your SwaggerConfiguration like that:
Take care to replace the package (which need to be the one
containing your REST controllers), the host, and the PATH you need
#Configuration
#EnableSwagger2
public class SwaggerConfiguration implements WebMvcConfigurer {
public static final String PATH = "/myapi";
#Bean
public Docket api() {
final var package = "com.julia.rest";
final var host = "localhost:8080";
return new Docket(DocumentationType.SWAGGER_2)
.host(host)
.select()
.apis(RequestHandlerSelectors.basePackage(package))
.paths(PathSelectors.any())
.build();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
final var apiDocs = "/v2/api-docs";
final var configUi = "/swagger-resources/configuration/ui";
final var configSecurity = "/swagger-resources/configuration/security";
final var resources = "/swagger-resources";
registry.addRedirectViewController(PATH + apiDocs, apiDocs).setKeepQueryParams(true);
registry.addRedirectViewController(PATH + resources, resources);
registry.addRedirectViewController(PATH + configUi, configUi);
registry.addRedirectViewController(PATH + configSecurity, configSecurity);
registry.addRedirectViewController(PATH, "/");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(PATH + "/**").addResourceLocations("classpath:/META-INF/resources/");
}
}
Another solution is by changing the spring-boot URL context-path:
Edit pour application.properties file:
server.servlet.context-path=/myapi
Or if you have an application.yml file:
server:
servlet:
context-path: /myapi
Warning: It will change the base path of all your web services, not only Swagger

I also have faced this problem and tried many possible resolutions, and nothings didn't help really.
In my case, I can't use any resource redirect as swagger must be accessible as locally as on google cloud by match path /api-docs/**. and on google cloud any resource redirection will be denied in my case. All resources must be loading also from this path
here is my solution:
springfox-swagger2 and springfox-swagger-ui of version 2.9.2
#EnableSwagger2
#Configuration
public class SwaggerCommonConfig implements WebMvcConfigurer {
public static final String PATH = "/api-docs";
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController(PATH, "/");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(PATH + "/**").addResourceLocations("classpath:/META-INF/resources/");
}
}
and as springfox don't have any possibilities to do it by another way, in my case, we just will create simple controller that will be translating resource requests from our custom path to standard springfox. (it's not very elegant part but as it is :))
#RestController
#RequestMapping(SwaggerGatewayCommonConfig.PATH)
#RequiredArgsConstructor
public class SwaggerController {
private final RestTemplate restTemplate;
private final static String V2_API_DOCS = "/v2/api-docs";
private final static String SWAGGER_RESOURCES_CONFIGURATION_UI = "/swagger-resources/configuration/ui";
private final static String SWAGGER_RESOURCES_CONFIGURATION_SECURITY = "/swagger-resources/configuration/security";
private final static String SWAGGER_RESOURCES = "/swagger-resources";
private final static Pattern pattern = Pattern.compile("http[s]*://([^/]+)", Pattern.CASE_INSENSITIVE);
#Value("${server.port}")
private String port;
#GetMapping(V2_API_DOCS)
#SuppressWarnings("unchecked")
public Map<String, Object> getV2ApiDocs(HttpServletRequest request) {
Matcher matcher = pattern.matcher(request.getRequestURL().toString());
matcher.find();
Map<String, Object> resp = (Map<String, Object>) restTemplate.getForObject(toLocalSwaggerUrl(V2_API_DOCS), Map.class);
//we have to replace standard host, to requested host. as swagger UI make api requests from this host
resp.put("host", matcher.group(1));
return resp;
}
#GetMapping(SWAGGER_RESOURCES_CONFIGURATION_UI)
public Object getSwaggerResourcesConfigurationUi() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES_CONFIGURATION_UI), Object.class);
}
#GetMapping(SWAGGER_RESOURCES_CONFIGURATION_SECURITY)
public Object getSwaggerResourcesConfigurationSecurity() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES_CONFIGURATION_SECURITY), Object.class);
}
#GetMapping(SWAGGER_RESOURCES)
public Object getSwaggerResources() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES), Object.class);
}
private String toLocalSwaggerUrl(String path) {
return "http://localhost:" + port + path;
}
}
I hope it will save time to somebody faced it also =)
Good luck

Swagger base access url is constructed from your base application path.So if you change your base application path , you will get the desired behavior.But also all your apis will be changed to that path. You can find how to change it here How to set base url for rest in spring boot? .
What you did was too change how swagger call other apis from your application, not to change his base url. There are some tricks to change the swagger base url without changing application base path (moving manually all swagger resources), but i do not recommend that.

Related

AWS Lambda + Spring, how to load application.yml

I have problem with customizing API gateway domain, for my restful app deployed on AWS lambda. Customized domain, works this way, that depending on basePath it chooses different APIs which finally touches Lambda. For example:
api.mycustomdomain.com/view/ping -> goes to application view with path /view/ping
api.mycustomdomain.com/admin/ping -> goes to application admin with path /admin/ping
I am using this example as boilerplate: https://github.com/awslabs/aws-serverless-java-container/tree/master/samples/spring/pet-store
What I would like to achieve is handler which depending on Host header strips prefix from request path.
I have prepared following application.yml file:
server:
contextPath: "/view"
productionHost: "api.mycustomdomain.com"
The problem/question is. How can I now load those into my Lambda function? Here is my naive try:
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {
SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler;
boolean isinitialized = false;
#Value("${server.contextPath}")
private String prefix;
#Value("${server.productionHost}")
private String productionHost;
public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
if(awsProxyRequest.getHeaders().get("Host").equals(productionHost))
awsProxyRequest.setPath(awsProxyRequest.getPath().substring(prefix.length()));
if (!isinitialized) {
isinitialized = true;
try {
handler = SpringLambdaContainerHandler.getAwsProxyHandler(PingPongApp.class);
} catch (ContainerInitializationException e) {
e.printStackTrace();
return null;
}
}
return handler.proxy(awsProxyRequest, context);
}
}
Obviously this doesn't work, LambdaHandler is working out of Spring context.
Any ideas how can I deal with that?
It seems you can not load those properties. Follow either of the 2 options given below.
1> You can add following bean in your configuration and that way you can autowire strings and use the way you are already using
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
2>
public AwsProxyResponse..{
#Autowired
private Environment env;
..
public AwsProxyResponse handleRequest{
..
String contextPath = env.getRequiredProperty(“server.contextPath”));
...
}
}

Api annotation's description is deprecated

In Swagger, the #Api annotation's description element is deprecated.
Deprecated.
Not used in 1.5.X, kept for legacy support.
Is there a newer way of providing the description?
I found two solutions for Spring Boot application:
1. Swagger 2 based:
Firstly, use the tags method for specify the tags definitions in your Docket bean:
#Configuration
#EnableSwagger2
public class Swagger2Config {
public static final String TAG_1 = "tag1";
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("my.package")).build()
.tags(new Tag(TAG_1, "Tag 1 description."))
// Other tags here...
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("My API").version("1.0.0").build();
}
}
After, in RestController just add the #Api annotation with one (or more) of the your tags:
#Api(tags = { SwaggerConfig.TAG_1 })
#RestController
#RequestMapping("tag1-domain")
public class Tag1RestController { ... }
2. Swagger 3 based (OpenAPI):
Similarly, use the addTagsItem method for specify the tags definitions in your OpenAPI bean:
#Configuration
public class OpenApiConfig {
public static final String TAG_1 = "tag1";
#Bean
public OpenAPI customOpenAPI() {
final Info info = new Info()
.title("My API")
.description("My API description.")
.version("1.0.0");
return new OpenAPI().components(new Components())
.addTagsItem(createTag(TAG_1, "Tag 1 description."))
// Other tags here...
.info(info);
}
private Tag createTag(String name, String description) {
final Tag tag = new Tag();
tag.setName(name);
tag.setDescription(description);
return tag;
}
}
Finally, in RestController just add the #Tag annotation:
#Tag(name = OpenApiConfig.TAG_1)
#RestController
#RequestMapping("tag1-domain")
public class Tag1RestController { ... }
This is the correct way to add description to your Swagger API documentation for Swagger v1.5:
#Api(tags = {"Swagger Resource"})
#SwaggerDefinition(tags = {
#Tag(name = "Swagger Resource", description = "Write description here")
})
public class ... {
}
The reason why it's deprecated is that previous Swagger versions (1.x) used the #Api description annotation to group operations.
In the Swagger 2.0 specification, the notion of tags was created and made a more flexible grouping mechanism. To be API compliant, the description field was retained so upgrades would be easy, but the correct way to add a description is though the tags attribute, which should reference a #Tag annotation. The #Tag allows you to provide a description and also external links, etc.
I tried above solutions but they didn't work for me.
To add a title and description to the documentation you create ApiInfo and Contact objects like in example below.
Then you simply add apiInfo object to your Swagger Docket.
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
#EnableSwagger2
#Configuration
public class SwaggerConfig {
private Contact contact = new Contact("", "", "");
private ApiInfo apiInfo = new ApiInfo(
"Backoffice API documentation",
"This page documents Backoffice RESTful Web Service Endpoints",
"1.0",
"",
contact,
"",
"");
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage(
PaymentsController.class.getPackage().getName()
))
.paths(PathSelectors.ant("/api/v1/payments" + "/**"))
.build()
.useDefaultResponseMessages(false)
.globalOperationParameters(
newArrayList(new ParameterBuilder()
.name("x-authorization")
.description("X-Authorization")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build()));
}
}
Above code produces a description like in a screenshot below.
I too wondered what to do about uses of the deprecated description (showing up as warnings in my IDE).
Well, on closer inspection it turned out that description is not used anywhere in Swagger UI. After that the solution (in our case*) became clear: simply remove those descriptions.
(*In our codebase, with clean class and method names etc, there was certainly no need for such "API descriptions" for the reader of the code. I would have tolerated having these bits of Swagger-related noise in the codebase if they added some value in Swagger UI, but since they didn't, the only sensible thing was to throw them away.)
I found that the following works by combining both the #Api and #Tag annotations building off of this answer.
The value within the tags field of the #Api annotation needs to match the value within the name field of the #Tag annotation.
#Api(tags = "Controller API")
#Tag(name = "Controller API", description = "This controller performs API operations")
public class ReportStatusConsumerController {
}
An old question but may help using swagger 3
#Configuration
#EnableSwagger2
public class SwaggerConfig {
// Swagger configuration
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo( this.apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("API Reference").version("1.0.0")
.description("something")
.license("Apache 2.0")
.build();
}
public void addResouceHandler(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

How to configure a default #RestController URI prefix for all controllers?

I know you can set the server.contextPath in application.properties to change the root context.
Also, I can add an additional context in the application config for Spring Boot like the following example (in Groovy) to add an "/api" to the URL mappings of the root context:
#Bean
ServletRegistrationBean dispatcherServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/")
reg.name = "dispatcherServlet"
reg.addInitParameter("contextConfigLocation", "")
reg.addUrlMappings("/api/*")
reg.loadOnStartup = 2
reg
}
}
I am trying to have a separate base URI "/api" specifically for web service calls, that I can leverage for security, etc. However using the above approach will mean that any of my URIs, web service or not, can be reached with "/" or "/api", and provides no concrete segregation.
Is anyone aware of a better approach to set a base path for all #RestController(s) using configuration, without having to formally prefix every controller with /api/? If I am forced to manually prefix the URI for each controller, it would be possible to mistakenly omit that and bypass my security measures specific to web services.
Here is a reference in Stack Overflow to the same type of question, which was never completely answered:
Spring Boot: Configure a url prefix for RestControllers
In continuation to the currently accepted solution the github issue addresses the same.
Spring 5.1 and above you can implement WebMvcConfigurer and override configurePathMatch method like below
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forAnnotation(RestController.class));
}
}
Now all the #RestControllers will have /api as the prefix path alongside the path configured.
Official Documentation
There's a new solution to solve this kind of problem available since Spring Boot 1.4.0.RC1 (Details see https://github.com/spring-projects/spring-boot/issues/5004)
The solution of Shahin ASkari disables parts of the Auto configuration, so might cause other problems.
The following solution takes his idea and integrates it properly into spring boot. For my case I wanted all RestControllers with the base path api, but still serve static content with the root path (f.e. angular webapp)
Edit: I summed it up in a blog post with a slightly improved version see https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/
#Configuration
public class WebConfig {
#Bean
public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
private final static String API_BASE_PATH = "api";
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
Class<?> beanType = method.getDeclaringClass();
RestController restApiController = beanType.getAnnotation(RestController.class);
if (restApiController != null) {
PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH)
.combine(mapping.getPatternsCondition());
mapping = new RequestMappingInfo(mapping.getName(), apiPattern,
mapping.getMethodsCondition(), mapping.getParamsCondition(),
mapping.getHeadersCondition(), mapping.getConsumesCondition(),
mapping.getProducesCondition(), mapping.getCustomCondition());
}
super.registerHandlerMethod(handler, method, mapping);
}
};
}
};
}
}
Also You can achieve the same result by configuring WebMVC like this:
#Configuration
public class PluginConfig implements WebMvcConfigurer {
public static final String PREFIX = "/myprefix";
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix(PREFIX, c -> c.isAnnotationPresent(MyCustomAnnotation.class));
}
}
Implement WebMvcConfigurer on any #Configuration class.
Override configurePathMatch method.
You can do many useful things with PathMatchConfigurer e.g. add prefix for several classes, that satisfy predicate conditions.
I had the same concern and was not a fan of the Spring EL option due to the issues documented and I wanted the prefix to be tightly controlled in the controllers but I did not want to depend on the developers doing the right thing.
There might be a better way these days but this is what I did. Can you guys see any downsides, I am still in the process of testing any side-effects.
Define a custom annotation.
This allows a developer to explicitly provide typed attributes such as int apiVersion(), String resourceName(). These values would be the basis of the prefix later.
Annotated rest controllers with this new annotation
Implemented a custom RequestMappingHandlerMapping
In the RequestMappingHandlerMapping, I could read the attribute of the custom annotation and modify the final RequestMappingInfo as I needed. Here are a few code snippets:
#Configuration
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new MyCustomRequestMappingHandlerMapping();
}
}
And in the MyCustomRequestMappingHandlerMapping, overwrite the registerHandlerMethod:
private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class);
public MyCustomRequestMappingHandlerMapping() {
super();
}
#Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
// find the class declaring this method
Class<?> beanType = method.getDeclaringClass();
// check for the My rest controller annotation
MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class);
if (myRestAnnotation != null) {
// this is a My annotated rest service, lets modify the URL mapping
PatternsRequestCondition oldPattern = mapping.getPatternsCondition();
// create a pattern such as /api/v${apiVersion}/${resourceName}
String urlPattern = String.format("/api/v%d/%s",
myRestAnnotation.apiVersion(),
myRestAnnotation.resourceName());
// create a new condition
PatternsRequestCondition apiPattern =
new PatternsRequestCondition(urlPattern);
// ask our condition to be the core, but import all settinsg from the old
// pattern
PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern);
myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}",
beanType, myRestAnnotation, oldPattern, updatedFinalPattern);
mapping = new RequestMappingInfo(
mapping.getName(),
updatedFinalPattern,
mapping.getMethodsCondition(),
mapping.getParamsCondition(),
mapping.getHeadersCondition(),
mapping.getConsumesCondition(),
mapping.getProducesCondition(),
mapping.getCustomCondition()
);
}
super.registerHandlerMethod(handler, method, mapping);
}
}
Slightly less verbose solution which doesn't duplicate the logic of checking the annotation, but only changes the mapping path:
private static final String API_PREFIX = "api";
#Bean
WebMvcRegistrationsAdapter restPrefixAppender() {
return new WebMvcRegistrationsAdapter() {
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping() {
#Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType);
if (mappingForMethod != null) {
return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod);
} else {
return null;
}
}
};
}
};
}
Side effects
Your error controller will also be mapped under /api/error, which breaks error handling (DispatcherServlet will still redirect errors to /error without prefix!).
Possible solution is to skip /error path when adding /api prefix in the code above (one more "if").
Someone has filed an issue in the Spring MVC Jira and come up with a nice solution, which I am now using. The idea is to use the Spring Expression Language in the prefix placed in each RestController file and to refer to a single property in the Spring Boot application.properties file.
Here is the link of the issue: https://jira.spring.io/browse/SPR-13882

Spring StandardTypeLocator in StandardEvaluationContext: registering new import prefixes for use in Thymeleaf templates

We are developing a Spring MVC (v4) web app using the Thymeleaf templating library in the view layer with the Thymeleaf SpringTemplateEngine providing SPEL support.
When we reference types in our templates (e.g. for access to static utility methods or enums) we have to include the fully qualified name as the Spring StandardEvaluationContext StandardTypeLocator only knows about the java.lang package by default. I can see in the Spring API that we need to add our own packages to the type locator using the registerImport(String prefix) method, but I can't work out how to get hold of the default evaluation context that is used in our templates to be able to do this.
I want to de-clutter our Thymeleaf HTML templates by replacing this sort of thing:
T(org.apache.commons.io.FileUtils).byteCountToDisplaySize(1024)
With:
T(FileUtils).byteCountToDisplaySize(1024)
I tried autowiring an EvaluationContext into a controller to see if I could get hold of it, but Spring tells me no qualifying beans are found. Any advice appreciated!
I'm using JAVA based spring config. So in the spring security config class (There must be a #EnableGlobalMethodSecurity(prePostEnabled = true) annotation) , I'm injecting a custom MethodSecurityExpressionHandler bean:
#Bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler() {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext evaluationContext = super.createEvaluationContextInternal(auth, mi);
//Register custom package paths, since StandardTypeLocator is only aware of "java.lang"
//So there will be no need to provide a fully qualified path
((StandardTypeLocator) evaluationContext.getTypeLocator()).registerImport("desired.path");
return evaluationContext;
}
};
expressionHandler.setPermissionEvaluator(new ExpressionAccessPermissionEvaluator()); //register some custom PermissionEvaluator if any
return expressionHandler;
}
}
Thanks
I am not sure if this solves your problem, but ThymeleafViewResolver class has addStaticVariable method which adds variables to the context before the view is processed.
I made a little test:
#Autowired
ThymeleafViewResolver thymeleafViewResolver;
#PostConstruct
public void postConstruct() {
thymeleafViewResolver.addStaticVariable("myUtil", new StringUtils());
}
With StringUtils like the below:
public class StringUtils {
public static String print() {
return "Printed";
}
}
And the view:
<div th:text="${T(some.package.StringUtils).print()}">Test</div>
<div th:text="${myUtil.print()}">Test</div>
Both worked fine. The latter will also work if your method is not static.
Hope it helps.
I managed to do this by wrapping the EngineContextFactory Class so I put as follow in my Thymeleaf config class:
#Bean
public SpringTemplateEngine springTemplateEngine(SpringResourceTemplateResolver templateResolver,
IDialect springSecurityDialect)
{
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setEngineContextFactory(engineContextFactory());
templateEngine.addDialect(springSecurityDialect);
return templateEngine;
}
private IEngineContextFactory engineContextFactory()
{
return new EngineContextFactoryWrapper()
// packages to register
.registerImport("java.util")
.registerImport("java.math")
.registerImport("com.mainsys.fhome.gui.util");
}
public static class EngineContextFactoryWrapper
implements IEngineContextFactory
{
private final IEngineContextFactory delegate;
private final List<String> typeLocatorPrefixes;
public EngineContextFactoryWrapper()
{
super();
delegate = new StandardEngineContextFactory();
typeLocatorPrefixes = new ArrayList<String>();
}
#Override
public IEngineContext createEngineContext(IEngineConfiguration configuration,
TemplateData templateData,
Map<String, Object> templateResolutionAttributes,
IContext context)
{
IEngineContext engineCtx = delegate.createEngineContext(configuration, templateData, templateResolutionAttributes, context);
EvaluationContext evaluationContext;
if (engineCtx.containsVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME))
{
evaluationContext = (EvaluationContext) engineCtx.getVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME);
}
else
{
evaluationContext = new ThymeleafEvaluationContextWrapper(new StandardEvaluationContext());
}
for (String prefix : typeLocatorPrefixes)
{
((StandardTypeLocator) evaluationContext.getTypeLocator()).registerImport(prefix);
}
return engineCtx;
}
public EngineContextFactoryWrapper registerImport(String prefix)
{
typeLocatorPrefixes.add(prefix);
return this;
}
}

Exists any <jaxws:endpoint /> annotation?

I'm using CXF with Spring to publish and to consume my WebServices in JBoss 5.1. All works fine.
However, there's a thing that's I think very tedious: to put a jaxws:endpoint tag for every WebService in applicationContext.xml.
There's realy no way to do that with annotations? Thanks for all.
As time pass, there arise some new possibilities.
Working with CXF/SpringBoot (SpringBoot: 1.2.3, CXF: 3.10, Spring: 4.1.6) there is a nice alternative in order to get rid of the jaxws:endpoint configuration in cxf-servlet.xml, as jonashackt pointed out in nabble.com. However, this solution is only possible if there is only one endpoint in the application (at least I did not succeed to configure more than one).
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ServletRegistrationBean dispatcherServlet() {
CXFServlet cxfServlet = new CXFServlet();
return new ServletRegistrationBean(cxfServlet, "/api/*");
}
#Bean(name="cxf")
public SpringBus springBus() {
return new SpringBus();
}
#Bean
public MyServicePortType myService() {
return new MyServiceImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), myService());
endpoint.publish("/MyService");
return endpoint;
}
Where MyServicePortType is a class having the #WebService annotation. This Endpoint is then called for URL's like "localhost:8080/api/MyService"
Of course these #Bean declarations may be placed in any other spring config class.
In contrary to the copied original solution I suggest to instantiate the Bus (cxf-Bean) by using the factory method instead of the direct "new SpringBus()":
BusFactory.newInstance().createBus()
There are some annotations to configure things that you can also put in <jaxws:endpoint>. An annotation to declare a CXF endpoint would be nice.
You can configure an endpoint using code instead of Spring XML. This can be handy if you have a lot of repetitive configuration that you can factor out. Or if you have certain endpoints configured differently in different environments.
For example:
#Autowired var authImpl: Auth = _
#Autowired var faultListener: FaultListener = _
def initWebServices() {
var sf: JaxWsServerFactoryBean = _
val propMap = mutable.HashMap[String, AnyRef]("org.apache.cxf.logging.FaultListener"->faultListener.asInstanceOf[AnyRef])
sf = new JaxWsServerFactoryBean
sf.setServiceBean(authImpl)
sf.setAddress("/auth")
sf.setServiceName(new QName("http://auth.ws.foo.com/", "auth", "AuthService"))
sf.setProperties(propMap)
sf.create
// more services...

Categories