Freemarker template location and Spring Batch Admin - freemarker

I am using Spring Batch Admin as a web frontend for my Spring Batch project together with Spring Boot.
Batch Admin provides some templates using Freemarker to set up the layout. I have added some more templates which are stored in src/main/webapp/web/layouts/html and the ressources are included in the packaging process into the .jar file.
When I start the app, my own layouts are not found ("layouts/html/myOwn.ftl not found" is the error message).
I can solve this by adding a FreeMarkerConfigurer like this:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath"><value>classpath:/WEB-INF/</value></property>
</bean>
However, when I do this, my own templates are found but the standard templates are gone (like layouts/html/home.ftl).
Is there a way to provide two paths or two template loaders such that the default template loader of Spring Batch Admin is not overwritten but used as a fallback?
Or is there any other solution like having the ressources in a specific place?

Thanks to #ddekany I came up with the following solution.
Necessary configuration for Freemarker:
<bean id="freemarkerConfig" class="org.springframework.batch.admin.web.freemarker.HippyFreeMarkerConfigurer">
<property
name="templateLoaderPaths"
value="classpath:/WEB-INF/web,classpath:/org/springframework/batch/admin/web"
/>
<property name="preferFileSystemAccess" value="false" />
<property name="freemarkerVariables">
<map>
<entry key="menuManager" value-ref="menuManager" />
</map>
</property>
<property name="freemarkerSettings">
<props>
<prop key="default_encoding">UTF-8</prop>
<prop key="output_encoding">UTF-8</prop>
</props>
</property>
</bean>
The first property templateLoaderPaths (observe the additional s) allows to specify multiple paths separated by commas. The two paths are my own path classpath:/WEB-INF/web and the path to the default Spring Boot Admin files classpath:/org/springframework/batch/admin/web.
The additional configuration for the menuManager is necessary as otherwise the menu entries from the navigation disappear.
The custom freemarker layout files are stored in the default location src/main/webapp/WEB-INF/web/layouts/html/ and to be visible by the template loader have to be included in the jar build via
<resources>
<!-- copy the Freemarker templates -->
<resource>
<targetPath>WEB-INF</targetPath>
<filtering>false</filtering>
<directory>${basedir}/src/main/webapp/WEB-INF</directory>
</resource>
</resources>
in the project's pom.xml.

Related

Freemarker 2.3.24 auto-escape and spring.ftl macros issue

I upgraded to Freemarker 2.3.24 in order to use the setting output_format as HTMLOutputFormat and enable the auto escape but when I use the spring.ftl to read values from property files I get "Using ?html (legacy escaping) is not allowed when auto-escaping is on with a markup output format (HTML), to avoid double-escaping mistakes." Does anyone knows how to integrate the Freemarker auto-escape with spring property file reader?
here is my config bean:
<bean id="freeMarkerConfigurer"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/views/"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="freemarkerSettings">
<props>
<prop key="output_format">HTMLOutputFormat</prop>
</props>
</property>
</bean>
and here is my test.ftl
<#import "/spring.ftl" as spring/>
<html>
<div>hello</div>
<p><#spring.message "welcome"/></p>
</html>
and I get this Error:
Using ?html (legacy escaping) is not allowed when auto-escaping is on with a markup output format (HTML), to avoid double-escaping mistakes.
As you have some "legacy" templates (from Spring), you should leave the global output_format alone. Instead, you should specify the output_format for the non-legacy templates only. That can be done in two ways. One is using "ftlh" file extension instead of "ftl" (assuming that you want HTML-escaping) and then setting recognize_standard_file_extensions to true. The other is using the template_configurations setting (see http://freemarker.org/docs/pgui_config_templateconfigurations.html) to specify some other name pattern to associate the output_format with (such as anything that doesn't match the Spring templates).

mybatis spring mvc application, getting Invalid bound statement (not found)

this is my first mybatis spring mvc application using spring 3.2.4, mybatis-spring-1.2.1
When i try to call my webservice i get the error::
org.springframework.web.util.NestedServletException: Request processing failed;
nested exception is org.apache.ibatis.binding.BindingException: Invalid bound
statement (not found):
org.mydomain.formulary.drugmaster.dao.DrugMasterDao.getDrugsWithAlert
I must be missing something obvious.
Thanks for any help
Here are my associated files:
applicationContext.xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="formularyDb" />
<property name="configLocation" value="file:/web/sites/drugformulary-spring/config/mybatis-config.xml" />
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mydomain.formulary.mappers" />
</bean>
<bean id="DrugMasterDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mydomain.formulary.drugmaster.dao.DrugMasterDao" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
mapper file --> /classes/org/mydomain/formulary/mappers/drugmasterDao.xml
<mapper namespace="org.mydomain.formulary.drugmaster.dao.DrugMasterDao">
<select id="getDrugsWithAlert" parameterType="int" resultType="org.mydomain.formulary.drug_master.model.DrugMasters">
Select drug_id,drug_name,drug_alert_date,drug_alert_source, rownum
from (select drug_id,drug_name,to_char(drug_alert_datetime,'MM/DD/YYYY') as drug_alert_date ,drug_alert_source, rownum
from drug_master
where drug_status ='A' and length(drug_alert) > 0
order by drug_alert_datetime DESC )
where
<if test="_parameter != null">
rownum < #{count}
</if>
</select>
</mapper>
mapper file --> /classes/org/mydomain/formulary/drugmaster/dao/DrugMasterDao.java
public interface DrugMasterDao {
public List<DrugMasters> getDrugsWithAlert(int count);
}
controller file --> /classes/org/mydomain/formulary/drugmaster/controller/DrugMasterController.java
#Controller
public class DrugMasterController {
#Autowired
DrugMasterService drugMasterService;
#RequestMapping(value = "/drugmaster/withalerts/count/{count}", method = RequestMethod.GET)
public String withAlerts(ModelMap model, #PathVariable int count) {
List<DrugMasters> drugs = drugMasterService.getDrugsWithAlert(count);
return null/*for now*/;
}
}
service file --> /classes/org/mydomain/formulary/drugmaster/service/DrugMasterServiceImpl.java
#Service
public class DrugMasterServiceImpl implements DrugMasterService {
#Autowired
DrugMasterDao drugMasterDao;
public List<DrugMasters> getDrugsWithAlert(int count){
return drugMasterDao.getDrugsWithAlert(count);
}
}
mybatis-configfile -->
<configuration>
<settings>
<setting name="cacheEnabled" value="false" />
<setting name="lazyLoadingEnabled" value="false" />
</settings>
</configuration>
I googled this answer when looking for my error. It's actually unrelated to OP's problem, but the exception is the same and this question's very visible in google.
In my case I forgot to change the mapper namespace
<mapper namespace="pl.my.package.MyNewMapper">
Which resulted in the same problem.
I had the same problem so after reading the configurations in this page.
https://mybatis.github.io/spring/mappers.html#scan
I saw that my configuration was correct. so debugging my application. found that my *mappers.xml files where not in the path. that expect must be.
I had the XML files in the same folder src "java" in my maven project. so when I build my applications the file were not copy to classes folder. So I have to move the xml files to folder "resources". and the fix the problem.
Because your xml not load in mybatis, MapperScannerConfigurer only scan interface, not xml.
Have two way:
<mappers>
<mapper resource="org/mydomain/formulary/mappers/drugmasterDao.xml"/>
</mappers>
or
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:org/mydomain/**/*.xml"/>
</bean>
i had similar problem for my spring-boot mybatis application.
The issue was mybatis couldn't find the configuration file. After adding
mybatis.config-location=classpath:mybatis-config.xml
in the application.properties file, issue got resolved. Looks like issue is always around configuration files/mapper files and statement names.
In my case, I had multiple DataSource and should set mappler locations for each SessionFactory.
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(mysqlDataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactory.setMapperLocations(resolver.getResources("classpath:mappers/**/*Mapper.xml"));
(There could be many reasons, my case is a bit rare & weird, it's hard to discover, so I'd like to add an answer here, just in case someone did the same thing as me.)
In my case, then reason is in IDEA, when create multi-level package for mapper file I input mybatis.mapper, which only create a single dir but with name contains ..
While I should actually input mybatis/mapper to create multi-level dir at once.
In these 2 cases, the dir are shown the same as mybatis.mapper in the project view of IDEA, so it took me quiet a while to figure out why ...
I resolved the same problem with a variant of the solution of Giovanni Perea(thank you). I have the .xml mapper files in the same folder with .java mapper files and I using maven with maven-resources-plugin.
In my solution I have add an execution in maven-resources-plugin for copy all the .xml mapper file to the correct location(same folder of the .class mapper files):
<execution>
<id>copy-mappers-xml</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/com/myapplication/mapper</outputDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/java/com/myapplication/mapper/</directory>
<filtering>false</filtering>
<includes>
<include>*.xml</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
More examples with maven-resources-plugin: Including and excluding files and directories
If you do not use maven-resources-plugin see:
https://stackoverflow.com/a/12446666/2473158
In my case it was a typo error in the id of the mapper xml statement e.g.
<select id="getDtaa" resultType="Data">
List<T> getData()
After changing the id in the XML to the correct function name it worked.
most likely the java method name and the XML block's name mismatches
e.g mapper.getUser()
<select id="getUsee" resultMap="student">
...........
<>
getUser obviously different from getUsee
Except for #Dariusz mentioned above, I've also forgotten to add mapper location.
<property name="mapperLocations" value="classpath:mybatis/mappers/*.xml"/>
in the spring configuration, taking a xml way for example, make sure that the mapper.xml files are located at the place assigned as the value of the property named mapperLocations. Once I had it at mappers/anotherfolder/*.xml. And that causes the pain.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="somepackage.model"/>
<property name="mapperLocations" value="classpath*:mappers/*.xml"/>
</bean>
Rename mapper/drugmasterDao.xml to mapper/DrugMasterDao.xml , the name has to match the *Dao.java file name otherwise it throws error
or
we have to create Ibatismapping.xml explictly and add mapper configuration there
Check if there is any overload method in mapper. Try to use a separate name instead.
I have also encountered this problem in my development.
In generall, if you are using xml configuration files for spring,myBatis and etc., then this problem is mostly caused by some mistake in your xml configuration files.
The most voted answer by Dariusz demonstrated that there maybe some problems in the myBatis xml configuration file, while in my case, I found that problems in spring xml configuration file can also result in this problem.
In the situation of this post, in the applicationContext.xml(which should be a configuration file of Spring), we can see a basePackage configuration for the MapperScannerConfigurer:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mydomain.formulary.mappers" />
</bean>
so if the value for this property is wrong(the package name is not right), it will also result in this problem.
2 Points to cover
Check whether the method name is the same in both the XML and Java methods.
Mapper:scan covers the required package. If you are using annotation then #MapperScan(com.xxx) must be in the defined path.
In my case, using a query in an annotation, i didn't give enough credit to the fact that the XML in the annotation gets interpreted as XML.
So if you have
#Select("select * from table where id <> 'blabla'")
The <> is messing up the XML. You need to put it in a CDATA section
#Select("select * from table where id <![CDATA[ <> ]]> 'blabla'")
I think the issue is that MyBatis can't find the XML mapper configuration file. In my project using Spring Boot with MyBatis-3, I configured the mapper location in the application.properties as shown below:
mybatis.mapper-locations=classpath:mappers/*.xml
If you're using application.yml instead of .properties:
mybatis:
mapper-locations: "classpath:mappers/*.xml"
DrugMasters attributes defines must associate with the drug_id,drug_name,drug_alert_date,drug_alert_source, rownum .

Weblogic Spring Dependency Injection from flat-files on the file-system

I have an application deployed into Oracle Weblogic 10.3. In my .ear files, I use spring injection from xml files in the ear files. I would like to also perform dependency injection from xml files placed on the server file-system. We do something similar to this in Karaf, where we place our configuration files for bundles in the conf directory in the server itself. Is there a similar way to do this in weblogic?
You can ask spring to read properties from file systems as below;
<!-- Reads application properties and uses them in the application context -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="locations">
<list>
<value>file:D:\somefolder\application.properties</value>
<value>classpath:com/foo/sp.properties</value>
</list>
</property>
</bean>

Spring application context external properties?

i have a Spring application and its working well so far. Now i want the properties file in an external config folder and not in the packed jar to change things without the need to repack. This is what i got:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- <property name="locations" value="classpath:/springcontext.properties"/> -->
<property name="locations" value ="config/springcontext.properties" />
The outcommented one is working and the other one i dont get to work :/ Can someone help?
Edit:
Thx 4 comments so far.
Maybe my question wasnt clear enough :). I perform a Maven build and everything will be packaged and i want this folder to be NOT in the package nut next to the outcomming jar and in this folder i want the properties file. possible?
You can try something like this:
<context:property-placeholder
location="${ext.properties.dir:classpath:}/servlet.properties" />
And define ext.properties.dir property in your application server / jvm, otherwise the default properties location "classpath:/" (i.e., classes dir of .jar or .war) would be used:
-Dext.properties.dir=file:/usr/local/etc/
BTW, very useful blog post.
You can use file prefix to load the external application context file some thing like this
<context:property-placeholder location="file:///C:/Applications/external/external.properties"/>
<context:property-placeholder location="classpath*:spring/*.properties" />
If you place it somewhere in the classpath in a directory named spring (change names/dirs accordingly), you can access with above
<property name="locations" value ="config/springcontext.properties" />
this will be pointing to web-inf/classes/config/springcontext.properties
This blog can help you. The trick is to use SpEL (spring expression language) to read the system properties like user.home, to read user home directory using SpEL you could use #{ systemProperties['user.home']} expression inside your bean elements. For example to access your properties file stored in your home directory you could use the following in your PropertyPlaceholderConfigurer, it worked for me.
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:#{ systemProperties['user.home']}/ur_folder/settings.properties</value>
</property>
</bean>
This question is kind of old, but wanted to share something which worked for me. Hope it will be useful for people who are searching for some information accessing properties in an external location.
This is what has worked for me.
Property file contents:
PROVIDER_URL=t3://localhost:8003,localhost:8004
applicationContext.xml file contents: (Spring 3.2.3)
Note: ${user.home} is a system property from OS.
<context:property-placeholder system-properties-mode="OVERRIDE" location="file:${user.home}/myapp/latest/bin/my-env.properties"/>
<bean id="appsclusterJndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
<prop key="java.naming.provider.url">${PROVIDER_URL}</prop>
</props>
</property>
</bean>
${PROVIDER_URL} got replaced with the value in the properties the file
One way to do it is to add your external config folder to the classpath of the java process. That's how I've often done it in the past.
<context:property-placeholder location="file:/apps/tomcat/ath/ath_conf/pcr.application.properties" />
This works for me.
Local development machine path is C:\apps\tomcat\ath\ath_conf and in server /apps/tomcat/ath/ath_conf
Both works for me

Spring PropertyPlaceholderConfigurer not replacing placeholder

I want to pass the WSDL url for an internal web service into my Spring beans.xml dynamically, using a PropertyPlaceHolderConfigurer.
Here's the scenario:
My web application is deployed in WebLogic 10.3.
The WSDL url is contained in a properties file that is located outside my application (directly under the corresponding domain folder, while my application is inside the autodeploy folder). I set the location of this properties file in my domain's setDomainEnv.cmd file like below:
set JAVA_PROPERTIES=%JAVA_PROPERTIES% %CLUSTER_PROPERTIES% -Dproperty.file.path.config=%DOMAIN_HOME%\Service.properties
This is what my Service.properties file contains:
Service.WSDL.PATH=http://localhost:8088/mockServiceSoap?WSDL
My Spring beans.xml configuration:----
<bean id="file.path" class="java.lang.System" factory-method="getProperty">
<constructor-arg index="0"><value>property.file.path.config</value></constructor-arg>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" ref="file.path"/>
</bean>
<bean id="myServiceId" class="com.test.service.ServiceImpl">
<property name="myServiceSoap">
<ref bean="myService"/>
</property>
</bean>
<bean id="myService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="com.test.service.ServiceSoap"/>
<property name="wsdlDocumentUrl" value="${Service.WSDL.PATH}"/>
</bean>
I enabled DEBUG log specifically for PPC and this is what I saw in my application log:
INFO org.springframework.beans.factory.config.PropertyPlaceholderConfigurer 178 - Loading properties file from URL [file:D:/bea10.3/user_projects/domains/my_domain/Service.properties]
So, it seems that although the Service.properties file is getting loaded by PPC, the ${Service.WSDL.PATH} is NOT getting replaced.
What am I doing wrong here?
Also, how can I find out if PPC tried replacing the value of the placeholder and with what value? I was hoping the log file would contain that info but there was nothing there.
Any help is appreciated.
I've figured out, that PropertyPlaceholderConfigurer needs to be declared first in the application Context file, otherwise there's no guarantee of the load order. It took me a few hours to realize this.
Try moving the "file.path" bean into the PropertyPlaceHolderConfigurer's location property.

Resources