How to close jbpm engine and release it's resources - java

I'm now using embed jbpm 6.2 as my common service, so i write a jar which includes the jbpm service, and then i used a URL Classloader to load it, it runs pretty well, but after i closed the jbpm engine and try to remove the jars, there are some jars i can't remove.
here is the code to initialize the jbpm engine in the jar
try {
emf = Persistence.createEntityManagerFactory(Constants.JBPM_JPA_UNIT_NAME);
DBUserGroupCallbackImpl callback = new DBUserGroupCallbackImpl(props);
RuntimeEnvironmentBuilder kBuilder = RuntimeEnvironmentBuilder.getDefault()
.classLoader(this.getClass().getClassLoader())
.entityManagerFactory(emf)
.userGroupCallback(callback)
.persistence(true);
String path = props.getProperty(Constants.BPMN_RESOURCE_PATH);
logger.debug("Get bpmn resource with path = {}", path);
if (!StringUtil.isEmpty(path)) {
String[] paths = path.split(";");
List<File> files = getFile(paths);
if (files != null && !files.isEmpty()) {
List<Resource> resources = getResource(files);
if (resources != null && !resources.isEmpty()) {
for (Resource resource : resources) {
kBuilder.addAsset(resource, ResourceType.BPMN2);
logger.info("Add asset to jbpm success, resource = {}", resource);
}
}
}
}
runtimeManager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(kBuilder.get());
runtime = runtimeManager.getRuntimeEngine(EmptyContext.get());
this.kiesession = runtime.getKieSession();
this.taskService = runtime.getTaskService();
here is the code to close the jbpm engine:
public void close() {
if (runtimeManager != null) {
runtimeManager.disposeRuntimeEngine(runtime);
runtimeManager.close();
}
if (kiesession != null) {
kiesession.destroy();
}
if (emf != null) {
emf.close();
}
}
Now i use the code like below to remove the jars:
jbpmService.close();
urlcloassloader.close();
System.gc();
// waiting 1min
FileUtil.forceDelete(dir)
Most of the jars has been removed, but i can't remove the drools-core-6.2.0.Final.jar and postgresql-42.0.0.jre7.jar, is anybody know why?

Related

Resourcebundles in tomcat deployment war not working as expected

I am experiencing a strange issue related to loading of resource bundles. After some debugging, i am able to find out the cause of the issue as resource bundles are loading in expected order. But, I query is what is causing it.
I have resource bundles named as below.
core_cs.properties
core_de.properties
core_en_GB.properties
core_en_US.properties
core_en.properties
core_fr.properties
When resource bundles are loaded by spring configuration, then base name should be
../core
../core_en
But, it is resulting in
../core_en
../core
Due to this, every locale is showing translated value from core_en resource bundle.
In project, i am adding core.jar as dependency in my web project.
Current Behaviour.
found == META-INF/resourcebundles/core_en for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en_GB.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_cs.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_de.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_es.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_fr.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_hu.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_it.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_nl.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_pl.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_sl.properties
found == META-INF/resourcebundles/core_en for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en_US.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en.properties
Expected loading should be
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_cs.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_de.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en.properties
found == META-INF/resourcebundles/core_en for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en_GB.properties
found == META-INF/resourcebundles/core_en for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_en_US.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_es.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_fr.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_hu.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_it.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_nl.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_pl.properties
found == META-INF/resourcebundles/core for file jar:file:/D:/Tomcat/WebProject/webapps/demo/WEB-INF/lib/core-SNAPSHOT.jar!/META-INF/resourcebundles/core_sl.properties
I am using PathMatchingResourcePatternResolver from springframework to resolve the path from classpath configuration
<bean id="messageSource" class="com.company.core.spring.support.ClasspathResourceBundleMessageSource">
<property name="basename" value="classpath*:/META-INF/resourcebundles/**/*.properties"/>
</bean>
ClasspathResourceBundleMessageSource is extending ResourceBundleMessageSource from springframework
public void setBasenames(final String... basenames) {
List<String> basenamesList = new ArrayList<>();
try {
for (String basename : basenames) {
LOGGER.info("==\t Base Name configured {}", basename);
Resource[] resources = (new PathMatchingResourcePatternResolver()).getResources(basename);
for (Resource resource : resources) {
LOGGER.info("==\t Name of resource found {}", resource.getURI());
String filename = resource.getURI().toString();
String newBasename = transformToBasename(filename);
if (newBasename != null && !basenamesList.contains(newBasename)) {
LOGGER.info("Adding resource bundle basename {}", newBasename);
basenamesList.add(newBasename);
}
}
}
// Add the basenames found
LOGGER.info("Resource bundle path:{}", basenamesList);
String[] basenamesArray = basenamesList.toArray(new String[]{});
super.setBasenames(basenamesArray);
} catch (Exception ex) {
LOGGER.error("Error setting base names", ex);
}
}
Another insight is that, if i build archive war locally it works. But, when archive is build by build server (jenkins), then it is not working as expected.
I found that JarFile.entries() method from java.util.jar.JarFile is returning list of files in unsorted order. I tested it with a small program.
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarFileTest {
public static void main(String[] args) {
File correctJarFile = new File("D:\\target\\budget.jar");
File wrongJarFile = new File("D:\\target\\budget2.jar");
try (JarFile jar = new JarFile(correctJarFile )) {
for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.endsWith(".properties")) {
System.out.println(entryPath);
}
}
}catch (IOException e) {
}
System.out.println("\n============ 2");
try (JarFile jar = new JarFile(wrongJarFile)) {
for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.endsWith(".properties")) {
System.out.println(entryPath);
}
}
}catch (IOException e) {
}
}
}
Above program result in below output.
META-INF/resourcebundles/core_cs.properties
META-INF/resourcebundles/core_de.properties
META-INF/resourcebundles/core_en.properties
META-INF/resourcebundles/core_en_GB.properties
META-INF/resourcebundles/core_en_US.properties
META-INF/resourcebundles/core_fr.properties
============ 2
META-INF/resourcebundles/core_en_GB.properties
META-INF/resourcebundles/core_cs.properties
META-INF/resourcebundles/core_de.properties
META-INF/resourcebundles/core_fr.properties
META-INF/resourcebundles/core_en_US.properties
META-INF/resourcebundles/core_en.properties
This is same with Java8 Stream API
try (JarFile jar = new JarFile(jarFile)) {
jar.stream().filter(entry -> entry.getName().endsWith(".properties")).forEach(System.out::println);
} catch (IOException e) {
}
System.out.println("\n============ 0");
try (JarFile jar = new JarFile(jarFile2)) {
jar.stream().filter(entry -> entry.getName().endsWith(".properties")).forEach(System.out::println);
} catch (IOException e) {
}
So, I have applied custom sorting to fix ordering in ZipFile as mentioned in this stackoverflow question

PathMatchingResourcePatternResolver - find reason that org.springframework.core.io.Resource is set to "file:/" for certain jar

I have some code below that is being used to scan for my batis mapper files. I'm scanning for a My Batis file, but I think my problem is more a Spring issue.
The code listed has been working fine, but when I add dependency to certain external library that is stored in my local maven repo, the PathMatchingResourcePatternResolver.getResources() method returns a resource of "file:/" for a specific jar. This is causing spring to search my entire C drive for mapper files.
I was able to determine what jar is causing this behavior but I'm not sure what the resolution is. Why is Spring setting the Resource to "file:/" ? I'm not sure what is wrong with this jar vs others that return a normal resource entry.
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
PathMatchingResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
Resource configLocation = patternResolver.getResource("classpath:mybatis/mybatis.config.xml");
//this line leads to the issue
Resource[] mapperLocations1 = patternResolver.getResources("classpath*:**/*Mapper.xml");
//this is from the Spring PathMatchingResourcePatternResolver class
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<Resource>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
if (equinoxResolveMethod != null) {
if (rootDirUrl.getProtocol().startsWith("bundle")) {
rootDirUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
rootDirResource = new UrlResource(rootDirUrl);
}
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
}
else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);

spring boot 2 properties configuration

I have some code that works properly on spring boot prior to 2 and I find it hard to convert it to work with spring boot 2.
Can somebody assist?
public static MutablePropertySources buildPropertySources(String propertyFile, String profile)
{
try
{
Properties properties = new Properties();
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
// load common properties
PropertySource<?> applicationYamlPropertySource = loader.load("properties", new ClassPathResource(propertyFile), null);
Map<String, Object> source = ((MapPropertySource) applicationYamlPropertySource).getSource();
properties.putAll(source);
// load profile properties
if (null != profile)
{
applicationYamlPropertySource = loader.load("properties", new ClassPathResource(propertyFile), profile);
if (null != applicationYamlPropertySource)
{
source = ((MapPropertySource) applicationYamlPropertySource).getSource();
properties.putAll(source);
}
}
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
}
catch (Exception e)
{
log.error("{} file cannot be found.", propertyFile);
return null;
}
}
public static <T> void handleConfigurationProperties(T bean, MutablePropertySources propertySources) throws BindException
{
ConfigurationProperties configurationProperties = bean.getClass().getAnnotation(ConfigurationProperties.class);
if (null != configurationProperties && null != propertySources)
{
String prefix = configurationProperties.prefix();
String value = configurationProperties.value();
if (null == value || value.isEmpty())
{
value = prefix;
}
PropertiesConfigurationFactory<?> configurationFactory = new PropertiesConfigurationFactory<>(bean);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName(value);
configurationFactory.bindPropertiesToTarget();
}
}
PropertiesConfigurationFactory doesnt exist anymore and the YamlPropertySourceLoader load method no longer accepts 3 parameters.
(the response is not the same either, when I have tried invoking the new method the response objects were wrapped instead of giving me the direct strings/integers etc...)
The PropertiesConfigurationFactory should be replaced with Binder class.
Binder class
Sample code:-
ConfigurationPropertySource source = new MapConfigurationPropertySource(
loadProperties(resource));
Binder binder = new Binder(source);
return binder.bind("initializr", InitializrProperties.class).get();
We were also using PropertiesConfigurationFactory to bind a POJO to a
prefix of the Environment. In 2.0, a brand new Binder API was
introduced that is more flexible and easier to use. Our binding that
took 10 lines of code could be reduced to 3 simple lines.
YamlPropertySourceLoader:-
Yes, this class has been changed in version 2. It doesn't accept the third parameter profile anymore. The method signature has been changed to return List<PropertySource<?>> rather than PropertySource<?>. If you are expecting single source, please get the first occurrence from the list.
Load the resource into one or more property sources. Implementations
may either return a list containing a single source, or in the case of
a multi-document format such as yaml a source for each document in the
resource.
Since there is no accepted answer yet, i post my full solution, which builds upon the answer from #nationquest:
private ConfigClass loadConfiguration(String path){
MutablePropertySources sources = new MutablePropertySources();
Resource res = new FileSystemResource(path);
PropertiesFactoryBean propFactory = new PropertiesFactoryBean();
propFactory.setLocation(res);
propFactory.setSingleton(false);
// resolve potential references to local environment variables
Properties properties = null;
try {
properties = propFactory.getObject();
for(String p : properties.stringPropertyNames()){
properties.setProperty(p, env.resolvePlaceholders(properties.getProperty(p)));
}
} catch (IOException e) {
e.printStackTrace();
}
sources.addLast(new PropertiesPropertySource("prefix", properties));
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
return new Binder(propertySource).bind("prefix", ConfigClass.class).get();
}
The last three lines are the relevant part here.
dirty code but this is how i solved it
https://github.com/mamaorha/easy-wire/tree/master/src/main/java/co/il/nmh/easy/wire/core/utils/properties

Multipart Upload Servlet 3.0 - temporary files not deleting

I have an upload servlet that is working great but leaves it's temporary files lying around. I am trying to use the part.delete() to clean them up as I go, but they are not deleting.
The docs say the container will delete them when it does GC. But even if I wait an hour and eventually shut the server down, they are still there...
What's the trick? It's Eclipse Kepler with Tomcat 7.0.47 on Windows for the moment. But production will be Linux.
Thanks
Code condensed substantially:
#MultipartConfig(location = "C:/tmp",
fileSizeThreshold=1024*1024*10, // 10MB
maxFileSize=1024*1024*10, // 10MB
maxRequestSize=1024*1024*50) // 50MB
#WebServlet("/upload.do")
...
for (Part part : request.getParts()) {
String filename = getFilename(part);
if(!(filename==null)){
part.write("/elsewhere/"+filename);
part.delete();
} else {
out.println("skip field...");
}
}
Hi you can create Servlet Listner like this
#WebListener
public class ContextListner implements ServletRequestListener, ServletContextListener {
public ContextListner() {
}
public void requestDestroyed(ServletRequestEvent sre) {
deleteDir(sre.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
public void contextInitialized(ServletContextEvent sce) {
}
public void contextDestroyed(ServletContextEvent sce) {
deleteDir(sce.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
public void requestInitialized(ServletRequestEvent sre) {
}
private void deleteDir(final String dirPath) {
if (null == dirPath)
return;
File dir = new File(dirPath);
if (dir.exists() && dir.isDirectory()) {
File[] files = dir.listFiles();
if (null != files) {
for (File file : files) {
file.delete();
}
}
}
}
}
And mark your servlet with annotation as mentioned below.
#WebListener(value = "ContextListner")
This will delete temp file under temp directory or your specified directory.
public void requestDestroyed(ServletRequestEvent sre) {
deleteDir(sre.getServletContext().getRealPath("") + File.separator + UploadConstants.TEMP_DIR);
}
This method get call after response send back to client.

Struts Java: how to read struts-config.xml from my java code?

I have a java web application working on struts. I want to access the data in the struts-config.xml file in runtime.
I tried to access it like a simple file, but it's unreachable to the application because it's outside the root directory of the app.
How does struts itself read the file? And how can I immitate it in runtime? I just need to read it like a simple xml file.
Thanks.
Struts does this simply by using ServletContext.
Because Struts ActionServlet extends HttpServlet, they simply do:
URL resource = getServletContext().getResource("/WEB-INF/struts-config.xml");
From there, You can get an InputStream and read the data from the resource.
Should the resource be null, the other alternative would be:
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = this.getClass().getClassLoader();
}
Enumeration e = loader.getResources(path);
if (e != null && e.hasMoreElements()) {
resource = (URL)e.nextElement();
}
The codes above is just simplified.
In Stuts2, method init_TraditionalXmlConfigurations in class org.apache.struts2.dispatcher.Dispatcher is responsible to init xml configurations. It will search 3 files, struts-default.xml,struts-plugin.xml,struts.xml(they are defined in constant variant DEFAULT_CONFIGURATION_PATHS).
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}
Then, in method loadConfigurationFiles, it will get all configuration files url:
try {
urls = getConfigurationUrls(fileName);
} catch (IOException ex) {
ioException = ex;
}
And the following implementation is how to get configuration files' url:
protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
}
public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
AggregateIterator<URL> iterator = new AggregateIterator<URL>();
iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
if (!iterator.hasNext() || aggregate) {
iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
}
if (!iterator.hasNext() || aggregate) {
ClassLoader cl = callingClass.getClassLoader();
if (cl != null) {
iterator.addEnumeration(cl.getResources(resourceName));
}
}
if (!iterator.hasNext() && (resourceName != null) && ((resourceName.length() == 0) || (resourceName.charAt(0) != '/'))) {
return getResources('/' + resourceName, callingClass, aggregate);
}
return iterator;
}
The code above is how struts loads configuration.
For you, if you want to load struts-config.xml manually, you can use the following code:
String filePath = "your struts-config.xml file path";
URL resource = Thread.currentThread().getContextClassLoader().getResource(filePath);
Then, you can read the file like a simple xml file.

Categories