HHH-12979 - Setting hibernate.javax.cache.uri property value as relative path causes an error

Resolve the hibernate.javax.cache.uri using the ClassLoaderService.

Strip the classpath:// prefix if not resolved by new URL() in ClassLoaderService.
This way, if a framework (e.g. Spring) has installed a handler for classpath://, the resource is resolved with its handler and class loader.
If not, we remove the classpath:// prefix and we resolve the resource with our classloader.
This commit is contained in:
Vlad Mihalcea 2018-10-18 10:30:17 +03:00
parent fecb12cff7
commit 52e72f5d38
7 changed files with 240 additions and 8 deletions

View File

@ -39,6 +39,8 @@ public class ClassLoaderServiceImpl implements ClassLoaderService {
private static final CoreMessageLogger log = CoreLogging.messageLogger( ClassLoaderServiceImpl.class );
private static final String CLASS_PATH_SCHEME = "classpath://";
private final ConcurrentMap<Class, ServiceLoader> serviceLoaders = new ConcurrentHashMap<Class, ServiceLoader>();
private volatile AggregatedClassLoader aggregatedClassLoader;
@ -147,6 +149,10 @@ public class ClassLoaderServiceImpl implements ClassLoaderService {
catch (Exception ignore) {
}
// if we couldn't find the resource containing a classpath:// prefix above, that means we don't have a URL
// handler for it. So let's remove the prefix and resolve against our class loader.
name = stripClasspathScheme( name );
try {
final URL url = getAggregatedClassLoader().getResource( name );
if ( url != null ) {
@ -182,6 +188,10 @@ public class ClassLoaderServiceImpl implements ClassLoaderService {
catch (Exception ignore) {
}
// if we couldn't find the resource containing a classpath:// prefix above, that means we don't have a URL
// handler for it. So let's remove the prefix and resolve against our class loader.
name = stripClasspathScheme( name );
try {
log.tracef( "trying via [ClassLoader.getResourceAsStream(\"%s\")]", name );
final InputStream stream = getAggregatedClassLoader().getResourceAsStream( name );
@ -271,6 +281,18 @@ public class ClassLoaderServiceImpl implements ClassLoaderService {
return aggregated;
}
private String stripClasspathScheme(String name) {
if ( name == null ) {
return null;
}
if ( name.startsWith( CLASS_PATH_SCHEME ) ) {
return name.substring( CLASS_PATH_SCHEME.length() );
}
return name;
}
@Override
public void stop() {
for ( ServiceLoader serviceLoader : serviceLoaders.values() ) {

View File

@ -8,8 +8,10 @@ package org.hibernate.cache.jcache.internal;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
@ -31,7 +33,6 @@ import org.hibernate.cache.spi.support.DomainDataStorageAccess;
import org.hibernate.cache.spi.support.RegionFactoryTemplate;
import org.hibernate.cache.spi.support.RegionNameQualifier;
import org.hibernate.cache.spi.support.StorageAccess;
import org.hibernate.cache.spi.support.DomainDataRegionImpl;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
@ -203,7 +204,7 @@ public class JCacheRegionFactory extends RegionFactoryTemplate {
final CachingProvider cachingProvider = getCachingProvider( properties );
final CacheManager cacheManager;
final URI cacheManagerUri = getUri( properties );
final URI cacheManagerUri = getUri( settings, properties );
if ( cacheManagerUri != null ) {
cacheManager = cachingProvider.getCacheManager( cacheManagerUri, getClassLoader( cachingProvider ));
}
@ -219,18 +220,25 @@ public class JCacheRegionFactory extends RegionFactoryTemplate {
return cachingProvider.getDefaultClassLoader();
}
@SuppressWarnings("WeakerAccess")
protected URI getUri(Map properties) {
protected URI getUri(SessionFactoryOptions settings, Map properties) {
String cacheManagerUri = getProp( properties, ConfigSettings.CONFIG_URI );
if ( cacheManagerUri == null ) {
return null;
}
try {
return new URI( cacheManagerUri );
URL url = settings.getServiceRegistry()
.getService( ClassLoaderService.class )
.locateResource( cacheManagerUri );
if ( url == null ) {
throw new CacheException( "Couldn't load URI from " + cacheManagerUri );
}
catch ( URISyntaxException e ) {
throw new CacheException( "Couldn't create URI from " + cacheManagerUri, e );
try {
return url.toURI();
}
catch (URISyntaxException e) {
throw new CacheException( "Couldn't load URI from " + cacheManagerUri, e );
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.test.cache.jcache.config;
import java.util.Map;
import org.hibernate.cache.jcache.ConfigSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
public class JCacheClasspathConfigUriTest
extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map settings) {
settings.put( Environment.CACHE_REGION_FACTORY, "jcache" );
settings.put( ConfigSettings.CONFIG_URI, "classpath://hibernate-config/ehcache/jcache-ehcache-config.xml" );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[]{
Product.class
};
}
@Test
public void test() {
Product product = new Product();
product.setName( "Acme" );
product.setPriceCents( 100L );
doInHibernate( this::sessionFactory, session -> {
session.persist( product );
} );
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.test.cache.jcache.config;
import java.util.Map;
import org.hibernate.cache.jcache.ConfigSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
public class JCacheConfigRelativePathTest
extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map settings) {
settings.put( Environment.CACHE_REGION_FACTORY, "jcache" );
settings.put( ConfigSettings.CONFIG_URI, "/hibernate-config/ehcache/jcache-ehcache-config.xml" );
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Product.class
};
}
@Test
public void test() {
Product product = new Product();
product.setName( "Acme" );
product.setPriceCents( 100L );
doInHibernate( this::sessionFactory, session -> {
session.persist( product );
} );
}
}

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.test.cache.jcache.config;
import java.util.Map;
import org.hibernate.cache.jcache.ConfigSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
public class JCacheConfigUrlTest
extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void addSettings(Map settings) {
settings.put( Environment.CACHE_REGION_FACTORY, "jcache" );
settings.put(
ConfigSettings.CONFIG_URI,
"file://" + Thread.currentThread().getContextClassLoader().getResource( "hibernate-config/ehcache/jcache-ehcache-config.xml" ).getPath()
);
}
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Product.class
};
}
@Test
public void test() {
Product product = new Product();
product.setName( "Acme" );
product.setPriceCents( 100L );
doInHibernate( this::sessionFactory, session -> {
session.persist( product );
} );
}
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.test.cache.jcache.config;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* @author Vlad Mihalcea
*/
@Entity(name = "Parent")
public class Product {
@Id
@GeneratedValue
private Long id;
private String name;
private Long priceCents;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getPriceCents() {
return priceCents;
}
public void setPriceCents(Long priceCents) {
this.priceCents = priceCents;
}
}

View File

@ -0,0 +1,13 @@
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">
<cache alias="ready-cache">
<key-type>java.lang.Long</key-type>
<value-type>org.hibernate.test.cache.jcache.config.Product</value-type>
<heap unit="entries">100</heap>
</cache>
</config>