El implementation is not found for hibernate-validator in osgi-container
Asked Answered
A

4

7

I try to run Hibernate validator in osgi container.

<dependency>
    <groupId>javax.el</groupId>
    <artifactId>javax.el-api</artifactId>
    <version>2.2.4</version>
</dependency>
<dependency>
    <groupId>org.glassfish.web</groupId>
    <artifactId>javax.el</artifactId>
    <version>2.2.4</version>
</dependency>

<dependency>
    <groupId>org.apache.servicemix.bundles</groupId>
    <artifactId>org.apache.servicemix.bundles.hibernate-validator</artifactId>
    <version>5.0.2.Final_1</version>
</dependency>

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.7</version>
</dependency>

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.8.1</version>
</dependency>


public class HibernateValidationProviderResolver implements ValidationProviderResolver {
    @Override
    public List<ValidationProvider<?>> getValidationProviders() {
        List<ValidationProvider<?>> list = new ArrayList<>(1);
        list.add(new HibernateValidator());
        return list;
    }
}


Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
    new HibernateValidationProviderResolver()
).configure();

ValidatorFactory validatorFactory = configuration.buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);


public class Group {
    @NotNull
    @Size(min=2)
    private String title;
}

Try to run, equinox console is okay:

10      RESOLVED    org.glassfish.web.javax.el_2.2.4
39      RESOLVED    org.apache.servicemix.bundles.hibernate-validator_5.0.2.Final_1
47      RESOLVED    javax.validation.api_1.1.0.Final
49      RESOLVED    javax.el-api_2.2.4

If I pass Group class instance with title = null, then validation is okay and constraintViolations contains one violation "not null". If I pass Group class instance with title = "A" (one character against minimal length = 2), then it throws an exception

Caused by: javax.el.ELException: Provider com.sun.el.ExpressionFactoryImpl not found
Caused by: java.lang.ClassNotFoundException: com.sun.el.ExpressionFactoryImpl

It 100% caused by osgi, but how I should setup hibernate-validator in osgi? All articles what I can found describes creating of HibernateValidationProviderResolver and that's all.

UPDATE 1

Maven: javax.el:javax.el-api:2.2.4

Export-Package: javax.el;version="2.2.4"
Import-Package: javax.el;version="2.2.4"

Maven: org.glassfish.web:javax.el:2.2.4 MANIFEST.MF

Export-Package: com.sun.el;uses:="javax.el";version="2.2.4"
Private-Package: com.sun.el.lang;version="2.2.4",com.sun.el.parser;version="2.2.4",com.sun.el.util;version="2.2.4"
Import-Package: com.sun.el;version="2.2.4",javax.el;version="2.2"

Maven: org.apache.servicemix.bundles:org.apache.servicemix.bundles.hibernate-validator:5.0.2.Final_1

Implementation-Version: 5.0.2.Final
Import-Package: javax.el,javax.persistence;resolution:=optional, ...

Export-Package: org.hibernate.validator.internal.engine.messageinterpola
tion.el;uses:="javax.el,javax.validation,org.hibernate.validator.intern
al.engine.messageinterpolation";version="5.0.2.Final",org.hibernate.val
idator.internal.engine.messageinterpolation;uses:="javax.validation.met
adata,org.hibernate.validator.internal.engine.messageinterpolation.el,j
avax.el,javax.validation,org.hibernate.validator.internal.util.logging"
;version="5.0.2.Final", ...

Any version for import in hibernate bundle, 2.2.4 in export of el-api and el-impl and el-impl imports el-api as 2.2, not a 2.2.4. All bundles are resolved.

Update 2

Decision 1

my implementation of @hwellmann's idea. @hwellmann, is it correct?

public void createGroup(Group group) {
    ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader();

    try {
        ClassLoader[] classLoaders = new ClassLoader[] {
            prevClassLoader,
            ExpressionFactoryImpl.class.getClassLoader()
        };

        // build composite classloader

        Thread.currentThread().setContextClassLoader(compositeClassLoader);

        Set<ConstraintViolation<Group>> constraintViolations = validator.validate(group);

    } finally {
        Thread.currentThread().setContextClassLoader(prevClassLoader);
    }
}

It works but looks strange. And change TCCL on each validation processing looks as some overhead.

Decision 2

The error has gone when I add my own message attribute to each validation annotation, for example for Group:

public class Group {
    @NotNull
    @Size(min=2, message="field.too_short")
    private String title;
}

It seems in this case hibernate interpolator is not started so ExpressionFactoryImpl is not retrieved from TCCL (previously we read min=2 value, now we don't). If it is okay for us, this decision is simplest. I will investigate this area futhermore and share my observations there.

Alight answered 16/4, 2015 at 14:40 Comment(2)
how's the import/export packages for the hibernate validation bundle? Might very well be that the validation bundle doesn't import the right package, or the el-bundle doesn't export those as expected.Su
@AchimNierbeck, thank you for your attention! I have updated the post. Now I'll try to change versions (el-api and el-impl) and implementations of el-api. May be it helps...Alight
I
1

The full stack trace might give some more insight than just the exception messages.

My guess is that you're seeing the results of a failed service lookup via META-INF/service/javax.el.ExpressionFactory. The bundle that's doing the lookup apparently can't see com.sun.el.

Importing this package into your application bundle and setting the thread context classloader to your bundle classloader might help.

Ine answered 18/4, 2015 at 12:53 Comment(1)
yep, you are right, changing of TCCL fix the issue. Please, look at my implementation in Update 2, it is my first time when I change TCCL in osgi, is it correct implementation? Should I do it on each validation (on the controller validation stage)? And of course, I think, I can accept your answer, thank you!Alight
M
4

There is a related issue in JBoss Fuse: https://access.redhat.com/solutions/1479723

They recommend a downgrade to 4.3.1.Final:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>4.3.1.Final</version>
</dependency>
Meliorate answered 2/8, 2016 at 14:53 Comment(1)
Pawel, it looks interesting. I have not discovered this link by search. Thank you for this answer.Alight
I
1

The full stack trace might give some more insight than just the exception messages.

My guess is that you're seeing the results of a failed service lookup via META-INF/service/javax.el.ExpressionFactory. The bundle that's doing the lookup apparently can't see com.sun.el.

Importing this package into your application bundle and setting the thread context classloader to your bundle classloader might help.

Ine answered 18/4, 2015 at 12:53 Comment(1)
yep, you are right, changing of TCCL fix the issue. Please, look at my implementation in Update 2, it is my first time when I change TCCL in osgi, is it correct implementation? Should I do it on each validation (on the controller validation stage)? And of course, I think, I can accept your answer, thank you!Alight
M
0

I had a similar issue with Hibernate 5.2.5 and Jetty 9.x. Basically, it looked like Hibernate didn't check if there was already an EL implementation on the classpath and tried to use javax.el-api instead of Jetty 9's apache-el. An upgrade to Hibernate 5.2.10 solved the issue for me. No conflicting el-api on the classpath any more.

Moravian answered 9/6, 2017 at 7:53 Comment(0)
O
0

Background

I use hibernate validator version 5.4.3.Final.

Starting from 5.3.1.Final, it mentions that ExpressionFactoryImpl is a hard requirement.

there is OSGi issue in 5.3.1.Final,issue is ClassLoader issues in modularized environments. It has been fixed in 5.3.3.Final. Ticket number isHV-1155

Fix

Based on the above information, there is no need to use Update 2 -> Decision 1. I found out that you could simply change the code posted by OP from

Configuration<?> configuration = Validation.byDefaultProvider().providerResolver(
    new HibernateValidationProviderResolver()
).configure();

to

Configuration<?> configuration = Validation.byProvider(HibernateValidator.class).providerResolver(
    new HibernateValidationProviderResolver()
).externalClassLoader(getClass().getClassLoader()).configure();

11.15. Customizing class-loading mentions this trick.

In addition, if you are using hibernate validator version 6.0.0.CR3 above, you don't need to apply the trick above.HV-1426 covers it.

Ostrogoth answered 27/10, 2021 at 20:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.