Spring java based config ResourceLoader - spring-mvc

I am new to java-based configuration in spring. I am trying to convert the xml based configuration found here: https://github.com/sps/mustache-spring-view
My problem is the ResourceLoader used by MustacheViewResolver is throwing a null pointer exception. How can I properly load the resource loader provided by spring into this configuration?
#Configuration
#ComponentScan(basePackageClasses = Application.class, includeFilters = #Filter(Controller.class), useDefaultFilters = false)
class WebMvcConfig extends WebMvcConfigurationSupport {
#Bean
public MustacheViewResolver viewResolver() {
MustacheViewResolver resolver = new MustacheViewResolver();
resolver.setPrefix("/WEB-INF/views/mustache/");
resolver.setSuffix("hbs");
resolver.setCache(true);
resolver.setTemplateFactory(new MustacheJTemplateFactory());
return resolver;
}
}
Exception:
Caused by: java.lang.NullPointerException
at org.springframework.web.servlet.view.mustache.java.MustacheJTemplateFactory.getReader(MustacheJTemplateFactory.java:71)
And the line from MustacheJTemplateFactory
Resource resource = resourceLoader.getResource(resourceName);
Please Note: I believe this to be a general question about spring java configuration, and not a specific question about the library I am using. I could be wrong though!

Autowire in the Spring resource loader:
#Autowired
ResourceLoader resourceLoader;
Then set it in your viewResolver() function:
MustacheJTemplateFactory factory = new MustacheJTemplateFactory();
factory.setResourceLoader(resourceLoader);
resolver.setTemplateFactory(factory);

Related

Initialising OAuth WebClient Bean in Spring MVC

I have a WebApp JSP project deployed on Weblogic 12 as a WAR.
My gradle build includes mvc and webflux:
implementation 'org.springframework.boot:spring-boot-starter-web:2.3.2.RELEASE'
implementation ("org.springframework.boot:spring-boot-starter-security:2.3.2.RELEASE")
implementation ("org.springframework.boot:spring-boot-starter-oauth2-client:2.3.2.RELEASE")
implementation ("org.springframework.boot:spring-boot-starter-webflux:2.3.2.RELEASE")
I am trying to configure OAuth2 to use client_credentials flow from my client JSP application.
I need the #Controller class to use WebClient and propagate the access token to a Resource Server.
My Bean to create the WebClient is seen below.
#Bean
public ReactiveClientRegistrationRepository getRegistration() {
ClientRegistration registration = ClientRegistration
.withRegistrationId("ei-gateway")
.tokenUri("https://xxxxx.xxxxxxx.net/auth/oauth/v2/token")
.clientId("xxx-xxxx-43e9-a407-xxxxx")
.clientSecret("xxxxxx-3d21-4905-b6e5-xxxxxxxxxx")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
}
#Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations, ServerOAuth2AuthorizedClientRepository authorizedClients) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
oauth.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.filter(oauth)
.defaultHeader("accept", "application/json")
.defaultHeader("content-type", "application/json")
.defaultHeader("environment", environment)
.filter(logRequest())
.filter(logResponse())
.build();
}
However I get the following error during compile:
Could not autowire. There is more than one bean of 'ReactiveClientRegistrationRepository' type.
Beans:
clientRegistrationRepository   (ReactiveOAuth2ClientConfigurations.class)
getRegistration   (WebSecurityConfiguration.java)
However when I uncomment out the getRegistration Bean method and configure the oauth client registration via the web.xml, then when deploying the application I get this error:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}:org.springframework.beans.factory.NoSuchBeanDefinitionException:No qualifying bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I see from the ReactiveOAuth2ClientAutoConfiguration source code that the Reactive OAuth2 Auto Configuration is not run when ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition is set.
#Configuration(proxyBeanMethods = false)
#AutoConfigureBefore(ReactiveSecurityAutoConfiguration.class)
#EnableConfigurationProperties(OAuth2ClientProperties.class)
#Conditional(ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.class)
#ConditionalOnClass({ Flux.class, EnableWebFluxSecurity.class, ClientRegistration.class })
#Import({ ReactiveOAuth2ClientConfigurations.ReactiveClientRegistrationRepositoryConfiguration.class,
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration.class })
public class ReactiveOAuth2ClientAutoConfiguration {
}
Can anyone suggest a course of action? Is is possible to manually configure the ReactiveOAuth2ClientConfiguration?
Thanks
Form what I understand ReactiveClientRegistrationRepository is not available since you are not using a reactive stack, and here's how you can set up WebClient to be used in a Servlet environment.
Setup application properties so Spring autowires ClientRegistrationRepository and OAuth2AuthorizedClientRepository for you.
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://xxxxx.xxxxxxx.net/auth/oauth/v2/token
spring.security.oauth2.client.registration.ei-gateway.client-id=xxx-xxxx-43e9-a407-xxxxx
spring.security.oauth2.client.registration.ei-gateway.client-xxxxxx-3d21-4905-b6e5-xxxxxxxxxx
spring.security.oauth2.client.registration.ei-gateway.provider=my-oauth-provider
spring.security.oauth2.client.registration.ei-gateway.scope=read,write
spring.security.oauth2.client.registration.ei-gateway.authorization-grant-type=client_credentials
Setup configuration to indicate that your application needs to act as an oauth2 Client
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.oauth2Client();
}
}
Expose WebClient bean configured to use client credentials
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
#Bean
WebClient webClient(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(
oAuth2AuthorizedClientManager);
// default registrationId - Only if you are not using the webClient to talk to different external APIs
oauth2Client.setDefaultClientRegistrationId("ei-gateway");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
Now you can use WebClient in your code to access external protected resources.
references:
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2Client-webclient-servlet
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#defaulting-the-authorized-client
This set up worked for me when the application is not configured as a resource server, I had to use a different configuration when the application needs to use WebClient, but also configured to be a resource server.

Using CXF with Spring Boot Actuator

I am working on a web service host application in which using cxf with spring boot. when I register cxf servlet with following code web service side works and I can see published wsdls.
However after setting cxf servlet Spring boot actuator and rest endpoints not working and returning 404. How can I solve this issue ?
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
Although I dont know the reason, when I set a name like below it starts working.
#Bean
public ServletRegistrationBean cxfServlet() {
ServletRegistrationBean cxf = new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
cxf.setName("cxfServlet");
return cxf;
}
Here is simple spring boot configuration I use.
#Configuration
#Import(value = { JaxRsConfig.class })
public class CxfRestConfig {
#Bean
public ServletRegistrationBean servletRegistrationBean() {
return new ServletRegistrationBean(new CXFServlet(), "/cxf/*");
}
#Component
public class CustomSpringComponentScanServer
extends AbstractSpringComponentScanServer {
#Override
protected String getAddress() {
return "/api";
}
#Bean
public Server jaxRsServer() {
super.getFeatures().add(new LoggingFeature());
return super.createJaxRsServer();
}
}
}
Note: With ComponentScanner you need to annotate your service class with Spring annotations along with #Path Annotation at class level.
If you do not want list of apis in http://localhost:8080/cxf you can directly remove the custom class I had written and you can import directly as shown below.
#Import(value = { JaxRsConfig.class, SpringComponentScanServer.class })
I was getting the same problem with Kotlin and this post indirectly helped me. My code was like this
#Bean
fun dispatcherServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
After changing the method name from dispatcherServlet to cxfServlet the actuator magically started to work.
#Bean
fun cxfServlet(): ServletRegistrationBean<CXFServlet>? {
return ServletRegistrationBean(CXFServlet(), "/*")
}
I guess it was conflicting with some Spring default servlet.
I looks like there is a clash between servlets.
You can check it in your logs. There should be:
2017-04-01 15:34:04,029 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'CXFServlet' to [/soap-api/*]
2017-04-01 15:34:04,031 [restartedMain] INFO o.s.b.w.s.ServletRegistrationBean - Mapping servlet: 'dispatcherServlet' to [/]
There should be exactly two servlets and the path should be different.
If there is one missing the enpoints won't work.
dispatcherServlet is spring default one to handle actuator metrics

How to pass IOptions to an ASP.NET 5 middle-ware service?

I have started playing around with ASP.NET 5 vNext and I am struggling passing the options from config.json into a middle-ware service that is used by my WebApi controller.
Here is a snippet with my middle-ware service:
public class MyService : IMyService
{
public MyService(IOptions<MyOptions> settings)
{
var o = settings.Options;
}
}
Here is my WebApi controller that is using the middle-ware service:
public class MyController : Controller
{
private IMyService _myService;
public TestController(IMyService service)
{
_myService = service;
}
}
In Startup.cs I am reading the options:
services.AddOptions();
services.Configure<MyOptions>(Configuration);
What I am struggling with is how to register an instance to IMyService so that it would be passed to the constructor of the controller (how can I get a hold of the IOptions)?
services.AddInstance<IMyService>(new MyService(XXXXX));
As suggested below I did try to use both
services.AddTransient<MyService>();
and
services.AddSingleton<MyService>();
But in both cases I am seeing the following error:
An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type
'MyApp.Services.IMyService' while attempting to activate
'MyApp.Controllers.TestController'.
Microsoft.Framework.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
Thanks for your help!
Don't register it as an Instance. Instead just add it as Scoped/Transient/Singleton depending on your requirements and let Dependency Injection do its magic;
services.Configure<MyOptions>(Configuration.GetSection("MyOptions"));
services.AddScoped<IMyService, MyService>();
For example, you can to add to Startup.cs that code:
public void ConfigureServices(IServiceCollection services)
{
var builder = new ConfigurationBuilder("[path to file with configuration]");
builder.AddJsonFile("config.json");
var config = builder.Build();
services.AddOptions();
services.Configure<MyOptions>(config);
//services.AddSingleton<IMyService, MyService>();
services.AddTransient<IMyService, MyService>();
}
I can assure you that you can use Singleton or Transient.
If you're interested, you can find more info here https://github.com/aspnet/Docs/issues/24.
And additionally, currently Autofac creates DI for ASP.NET 5 on
http://alexmg.com/autofac-4-0-alpha-1-for-asp-net-5-0-beta-3/

Importing spring.ftl using Spring MVC, Sitemesh, Freemarker

How can I import the spring.ftl macros into a Freemarker template page using Spring MVC, Sitemesh, and Freemarker?
I've configured a Spring MVC app using Sitemesh and Freemarker based on Ted Young's configuration example. According to the Spring MVC/Freemarker integration reference, it is necessary to import the spring.ftl macros in order to bind the backing model to the view via <#spring.bind "command.name"/>. However, doing this:
<#import "/spring.ftl" as spring>
<#spring.bind "command.user"/>
Results in this exception:
org.springframework.web.util.NestedServletException:
Request processing failed; nested exception is freemarker.
template.TemplateException: Error reading imported file spring.ftl
Others have experienced this issue, but I've yet to find a solution in google land. I also attempted to use this technique (zipping up spring.ftl, placing it in META-INF/lib, and adding the zip to the build path), but it didn't seem to work out.
Thanks!
The problem is that spring dont know where to look after the spring.ftl file:
This is my custom configuration for an MVC project using Boot
/**
* Otras configuraciones de la aplicaciones web, incluyendo algunas definidas en
* xml. Usar #ImportResource("classpath:/extra-config.xml") en caso de quererse
* importar configuracion en xml
*/
#Configuration
#PropertySource("classpath:application.properties")
public class WebAppConfig
{
#Autowired
private ServletContext context;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer placeHolderConfigurer = new PropertySourcesPlaceholderConfigurer();
return placeHolderConfigurer;
}
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() throws IOException, TemplateException
{
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer()
{
#Override
protected void postProcessConfiguration(freemarker.template.Configuration config) throws IOException, TemplateException
{
WebappTemplateLoader WebAppTplLoader = new WebappTemplateLoader(context, "/WEB-INF/ftl");
ClassTemplateLoader classTplLoader = new ClassTemplateLoader(context.getClassLoader(), "/templates");
ClassTemplateLoader baseMvcTplLoader = new ClassTemplateLoader(FreeMarkerConfigurer.class, "");
MultiTemplateLoader mtl = new MultiTemplateLoader(new TemplateLoader[]
{
WebAppTplLoader,
classTplLoader,
baseMvcTplLoader
});
config.setTemplateLoader(mtl);
}
};
configurer.setDefaultEncoding("UTF-8");
configurer.setPreferFileSystemAccess(false);
return configurer;
}
#Bean
public FreeMarkerViewResolver viewResolver()
{
FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
viewResolver.setExposeSpringMacroHelpers(true);
viewResolver.setExposeRequestAttributes(true);
viewResolver.setPrefix("");
viewResolver.setSuffix(".ftl");
viewResolver.setContentType("text/html;charset=UTF-8");
return viewResolver;
}
}
The first 2 loaders allow to load .ftl templates in war files from "/WEB-INF/ftl" and from regular jar files from src/resources/templates.
If you want to use security tags in freemarker the escense are this two lines:
viewResolver.setExposeSpringMacroHelpers(true);
viewResolver.setExposeRequestAttributes(true);
And the baseMvcTplLoader loader to get the spring.ftl from org.springframework.web.servlet.view.freemarker. I advice to explore ftl templates in some example project or documentation to have a clue of how spring.ftl works.
The configuration of the placeholder is not related to the freemarker
configuration, yet its very useful for injecting values in variables
from src/resources/application.properties by using the #Value
annotation.
With this you can use all the spring power within freemarker templates.
I like my spring.ftl included by default without having to add it manually within each view. In your configuration.
Define your freemarkerConfigurer as such.
#Bean(name = "freemarkerConfig")
public FreeMarkerConfigurer freemarkerConfig() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/views/");
Map<String, Object> map = new HashMap<>();
map.put("xml_escape", new XmlEscape());
configurer.setFreemarkerVariables(map)
def settings = new Properties()
settings['auto_import'] = 'spring.ftl as spring, layout/application.ftl as l'
configurer.setFreemarkerSettings(settings)
println "returning freemarker config"
return configurer;
}
<#import "spring.ftl" as spring/>
Without /

EJB3.1 classpath issue

I have 2 POJOs, in which one of them is an EJB and the other is a helper class.
//EJB Bean class
#Singleton
#LocalBean
#Startup
public class EJBBean{
#PostConstruct
public void init(){
HelperClass helper = new HelperClass();
helper.init();
}
}
//Helper class
public class HelperClass{
private static Log LOG = LogFactory.getLog("HelperClass");
private static Long currentTime = new Date().getTime();
public void init(){
//Some statements that use Log and do other Initialization
}
}
When I deploy this EJB jar I am getting an error
java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory
I have the commons-logging-1.1.1.jar in the classpath; also, I have configured it to use Log4J. As a standalone app that is without EJB meta-data it works fine. Am I missing some EJB config?
BTW I am pretty new to EJB. I am using GlassFish 3.1, Eclipse Helios as IDE and EJB3.1.
This could be because you put the commons-logging-1.1.1.jar into the wrong directory or because your server already provides server-wide library which consists of logging classes.
By the way - I remember a lot of strange 'NoClassDefFoundError' because of mixing commons-logging, log4j and slf4j (especially in mismatching versions).

Resources