UPDATE(November/2014):
Just added multiple datasources support to our example application, see here details: https://rpestano.wordpress.com/2014/11/04/cdi-crud-multi-tenancy/
<END UPDATE>
UPDATE(September/2014):
I’ve revisited this post some days ago and refactored the code, of course i keeped everything here and the github repo untouched so you can folow this post. Instead i’ve created a new github repository to share the new code there, i have updated the apis, added some (arquillian)tests, new functionality like true pagination and new apis(hibernate and deltaspike) and now it works on wildfly, Glassfish and Jboss7. I’ve also changed a bit of the project structure but the idea is the same, the code can be found here: cdi-crud.
<END UPDATE>
In this post i will show how to implement basic CRUD operations using a generic dao based on CDI beans. Its not the purpose to discuss about the Dao pattern itself, there are already long discussions about it see[1],[2],[3] and [4]. For a good and detailed introduction about the pattern see [5].
The source code can be found here: https://github.com/rmpestano/cdi-generic-dao and also as usual there is a video produced by this post here: http://www.youtube.com/watch?v=9mGQx0tjxgo&feature=youtu.be
Show me the code
lets get hands dirty with some code, here is the classic BaseEntity which in this case will hold only the primary key of our JPA entities:
@MappedSuperclass public abstract class BaseEntity<ID> { @Id @GeneratedValue(strategy = GenerationType.AUTO) private ID id; public ID getId() { return id; } public void setId(ID id) { this.id = id; } }
Next is the simple Car entity:
@Entity public class Car extends BaseEntity<Integer>{ private String model; private Double price; //getter & setters
Now the so called generic dao:
@Stateless @Named("baseDao") public class<T extends BaseEntity<ID>, ID>> implements Serializable { @PersistenceContext private EntityManager entityManager; private Class entityClass; public EntityManager getEntityManager() { return entityManager; } public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public Class getEntityClass() { if (entityClass == null) { //only works if one extends BaseDao, we will take care of it with CDI entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } return entityClass; } public void setEntityClass(Class entityClass) { this.entityClass = entityClass; } //utility database methods @TransactionAttribute(TransactionAttributeType.SUPPORTS) public T find(ID id) { return (T) this.entityManager.find(getEntityClass(), id); } public void delete(ID id) { Object ref = this.entityManager.getReference(getEntityClass(), id); this.entityManager.remove(ref); } public T update(T t) { return (T) this.entityManager.merge(t); } public void insert(T t) { this.entityManager.persist(t); } @TransactionAttribute(TransactionAttributeType.SUPPORTS) public List findAll() { return entityManager.createQuery("Select entity FROM "+getEntityClass().getSimpleName() +" entity").getResultList(); } //more utility methods
It is an usual dao, it has a PersistenceContext injected by the EJB container, it’s a Stateless Session Bean, have some utility database methods but it cannot work alone, you need to feed it with an Entity so it can perform database operations.
There are several ways to provide an entity to a generic dao, via constructor, annotation, extending it etc. In this example we provide the Entity by two manners, extending it and via CDI producer.
Extending the Dao
Here is a generic dao specialization, the CarDao:
@Stateless public class CarDao extends BaseDao<Car, Integer>{ //put here specific car business }
The entity is passed to BaseDao in getEntityClass() via ParameterizedType and now we can inject our dao in any bean:
public class SomeBean{ @Inject CarDao carDao; public void createCar(Car car){ carDao.insert(car); } }
this is good cause you usually will extend the base dao to add some more complex operations then CRUD but sometimes you just need the CRUD and you will have to create empty daos extending the BaseDao just to benefit from its utility methods, what i really want is to inject the BaseDao directly:
@Inject BaseDao<Car,Integer> baseCarDao;
The only thing that prohibits me from doing that is that the ParameterizedType can only be got from a superclass with getGenericSuperclass()(the only way I know 😉 ), at least before CDI came.
Producing the Dao
We are going to set the entity in a CDI producer, to do that we provide a qualifier that will tell CDI we want a produced Dao not the real one, here is the Dao qualifier:
@Qualifier @Retention(RUNTIME) @Target({FIELD,PARAMETER,METHOD,TYPE}) public @interface Dao { }
And here is our producer responsible of creating a baseDao with a setted Entity based on the InjectionPoint ParameterizedType:
public class DaoProducer implements Serializable { private static final long serialVersionUID = 1L; @Produces @Dependent//must be dependent pseudoScope cause baseDao is a SLB @Dao public <ID, T extends BaseEntity> BaseDao<T, ID> produce(InjectionPoint ip, BeanManager bm) { if (ip.getAnnotated().isAnnotationPresent(Dao.class)) { BaseDao<T, ID> genericDao = (BaseDao<T, ID>) this.getBeanByName("baseDao", bm);//ask bean manager for a instance of GenericDao ParameterizedType type = (ParameterizedType) ip.getType(); Type[] typeArgs = type.getActualTypeArguments(); Class<T> entityClass = (Class<T>) typeArgs[0]; genericDao.setEntityClass(entityClass); return genericDao; } throw new IllegalArgumentException("Annotation @Dao is required when injecting BaseDao"); } public Object getBeanByName(String name, BeanManager bm) { // eg. name=availableCountryDao{ Bean bean = bm.getBeans(name).iterator().next(); CreationalContext ctx = bm.createCreationalContext(bean); // could be inlined below Object o = bm.getReference(bean, bean.getBeanClass(), ctx); // could be inlined with return return o; } }
The ‘secret’ here is that we are infering the Dao’s entity in InjectionPoint so when we Inject BaseDao with:
@Inject @Dao BaseDao<User,Long> baseUserDao;
the (Class<T>) typeArgs[0]; in line 13 will return User.class and we set it in the Dao. Below is a concrete bean using our Dao to perform crud operations:
@Named @ViewAccessScoped public class CarBean implements Serializable{ private List carList; private List filteredValue;//datatable filteredValue attribute private Integer id; private Car car; @Inject CarDao carDao; @Inject @Dao BaseDao<Car,Integer> genericDao;//reuse generic dao for basic crud operation in various entities // @Inject @Dao // BaseDao<Person,Long> genericDao; // @Inject @Dao // BaseDao<Client,IDClass> genericDao; @PostConstruct public void init(){ if(genericDao.findAll().isEmpty()){ for (int i = 1; i < 10; i++) { Car c = new Car("Car"+i, i); genericDao.insert(c); } } //same as above // if(carDao.findAll().isEmpty()){ // for (int i = 0; i < 10; i++) { // Car c = new Car("Car"+i, i); // carDao.insert(c); // } // } } public List getCarList(){ if(carList == null){ carList = carDao.findAll(); } return carList; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Car getCar() { if(car == null){ car = new Car(); } return car; } public void setCar(Car car) { this.car = car; } public void findCarById(Integer id){ car = genericDao.find(id); } public List getFilteredValue() { return filteredValue; } public void setFilteredValue(List filteredValue) { this.filteredValue = filteredValue; } public void remove(){ if(car != null && car.getId() != null){ genericDao.delete(car.getId()); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Car "+car.getModel() +" removed successfully")); clear(); } } public void update(){ String msg; if(car.getId() == null){ genericDao.insert(car); msg = "Car "+car.getModel() +" created successfully"; } else{ genericDao.update(car); msg = "Car "+car.getModel() +" updated successfully"; } FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(msg)); clear();//reload car list } public void clear(){ car = new Car(); carList = null; id = null; } public void onRowSelect(SelectEvent event) { setId(((Car) event.getObject()).getId()); findCarById(getId()); } public void onRowUnselect(UnselectEvent event) { car = new Car(); } }
Addendum
Just a small addendum(which is not that small) there was an issue with the above implementation which showed up when using BaseDao to crud more than one entity in the same bean. The bug was caused because i was storing EntityClass in a stateless session bean(BaseDao), although CDI produces different instances for each injection point the container was returning the same session bean(from the pool of BaseDaos) for each produced bean. The solution was to remove EntityClass from BaseDao and put it in a CDI Bean called CrudDao which has a composion association with BaseDao making it really stateless(as it should be).Here is the CrudDao:
@Named("crudDao") public class CrudDao<T extends BaseEntity<ID>, ID> implements Serializable{ @Inject protected BaseDao<T,ID> dao; protected Class<T> entityClass; public Class<T> getEntityClass() { if (entityClass == null) { //only works if one extends CrudDao, we will take care of it with CDI entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; } return entityClass; } public void setEntityClass(Class<T> entityClass) { this.entityClass = entityClass; } public EntityManager getEntityManager(){ return dao.getEntityManager(); } public T find(ID id){ return dao.find(id, getEntityClass()); } public void delete(ID id){ dao.delete(id, getEntityClass()); } public T update(T t){ return dao.update(t); } public void insert(T t){ dao.insert(t); } public List<T> findAll(){ return dao.findAll(getEntityClass()); } public List<T> findWithNamedQuery(String namedQueryName){ return dao.findWithNamedQuery(namedQueryName); } }
References:
[1]http://www.adam-bien.com/roller/abien/entry/generic_crud_service_aka_dao
[2http://www.infoq.com/news/2007/09/jpa-dao
[3]http://www.adam-bien.com/roller/abien/entry/jpa_ejb3_killed_the_dao
[4]http://www.rponte.com.br/2009/06/08/no-more-daos/ [PT_BR]
[5]]http://tutorials.jenkov.com/java-persistence/dao-design-pattern.html
Spring Data JpaRepository does this stuff for free
LikeLike
True. In a project that has Spring framework, it’s the best solution. But he’s using CDI and EJB as example. In my opinion, dosen’t need the EJB. CDI provides all that you need, including the injection and/or extension.
LikeLike
yes they are competing models and in many many ways cdi is much much nicer as it can work across all layers. Still at the moment things like container managed transactions are in the ejb realm but that is going to eventually change. It just takes a long time 😉
LikeLike
Hi Arthur,
EJB is taking care of transactions here, you can do it with CDI only but you have to manually control commit/rollbacks… Myfaces CODI and Apache DeltaSpike have transaction management. Here i have a project using CODI for transactions:https://github.com/rmpestano/jsf-issuetracker-project so i can run it in a servlet container like tomcat/jetty.
thanks for your comment.
LikeLike
Agreed. The only thing that EJB has already that CDI doesen’t it’s the Transaction Management. But, you can do yours in CDI and you don’t need an EJB Container for that. You’ve to create your annotation and register the interceptor in the CDI Context that manages the transactions, but it’s not that hard. And you don’t need the heavy EJB Container, “only” to handle the transactions, right?
Or am i saying something really messed up? =P
LikeLike
more or less 😉 transaction management is not that simple, you have to take care of nested transactions for example but it is doable, see here:https://github.com/apache/deltaspike/tree/master/deltaspike/modules/jpa/impl/src/main/java/org/apache/deltaspike/jpa/impl/transaction/context
Im not sure declarative transactions are available in JavaEE7, see slide 23 here: http://www.slideshare.net/reza_rahman/javaeenext-java-ee-7-8-and-beyond
LikeLike
Hm. I’ll study more about it. It’s true that i prefere CODI to do that, because it’s already done. But i confess that i’ve only seen the simple and easy cases when i studied CDI.
Thanks for the tips guys.
LikeLike
This looks interesting http://ctpconsulting.github.com/query/1.0.0.Alpha5/index.html I have not tried it but it appears to be very similair to sping data but in cdi for java ee.
Cheers Magnus
LikeLike
Hi sugam,
do you have any example? as a newbie in spring i google it and found this post: http://nurkiewicz.blogspot.com.br/2013/01/spring-data-jdbc-generic-dao.html
looking at it i see a UserRepository which is extending JdbcRepository
can you for example Inject JdbcRepository directly with Spring?
something like @Autowire JdbcRepository<User,Long>genericRepository?
Also note that everything i posted here is for free 😉
thanks for your comment!
LikeLike
Here is a little example
https://gist.github.com/MagnusSmith/6000393#file-jparepository-example
There is a tutorial series here which is pretty good
http://www.petrikainulainen.net/spring-data-jpa-tutorial
Cheers
Magnus
LikeLike
Hi Magnus,
my question still remains, can you inject JpaRepository<User,Long> jpaRepository directly instead of extending it?
thanks for your contribution.
LikeLike
Hi Rafeal,
You don’t inject the repository directly you extend the interface and inject that new interface. This allows the Spring Data infrastructure to scan the classpath and create a spring bean for it. You don’t physically implement the interface yourself so injecting an interface that extends the repository should not make any difference to you. By extending the interface you can add attribute finder methods that Spring Data will then implement for you. (This was what I meant by for free – that Spring Data provides the implementations for you without you having to write any implementation code) 🙂
LikeLike
I see(im not so newbie with Spring, already used Spring data ;)), that’s what i’m doing with CarDao, i just extend BaseDao and add no implementation code. what i really focus on this post is to avoid ’empty daos'(or empty Repositories in your case) and i can do that Injecting the generic implementation directly.
Also note that empty daos are not common you usually will add some extra functionality, even when cruding.
Thanks again!
LikeLike
Hi,
Did you by any chance test on wildfly CR1?
I tried setting up a project, but I’m getting the same thing, that I think, you wrote about here https://community.jboss.org/thread/230420.
thanks
LikeLike
Hi Radu, didnt tried Wildfly yet, but i’ll take a look soon
LikeLike
During deploy on Glassfish 4 I get following exception:
2014-05-16T14:21:55.295+0200|Info: Computed the following CODI ProjectStage: Production
2014-05-16T14:21:55.700+0200|Severe: Exception during lifecycle processing
org.glassfish.deployment.common.DeploymentException: CDI deployment failure:WELD-001408 Unsatisfied dependencies for type [Validator] with qualifiers [@Default] at injection point [[UnbackedAnnotatedField] @Inject private org.hibernate.validator.internal.cdi.interceptor.ValidationInterceptor.validator]
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:225)
at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:131)
at org.glassfish.internal.data.ApplicationInfo.load(ApplicationInfo.java:328)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:493)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:527)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:523)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:356)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:522)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:546)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1423)
at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1500(CommandRunnerImpl.java:108)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1762)
at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1674)
at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:534)
at com.sun.enterprise.v3.admin.AdminAdapter.onMissingResource(AdminAdapter.java:224)
at org.glassfish.grizzly.http.server.StaticHttpHandler.service(StaticHttpHandler.java:297)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:246)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Validator] with qualifiers [@Default] at injection point [[UnbackedAnnotatedField] @Inject private org.hibernate.validator.internal.cdi.interceptor.ValidationInterceptor.validator]
at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:403)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:325)
at org.jboss.weld.bootstrap.Validator.validateInterceptor(Validator.java:554)
at org.jboss.weld.bootstrap.Validator.validateInterceptors(Validator.java:530)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:479)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:536)
at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:216)
… 36 more
LikeLike
GF4 doesnt play well with custom scopes, see this thread, it also contains the solution – the null check: http://mail-archives.apache.org/mod_mbox/myfaces-users/201403.mbox/%3CCAAuOd=VfKZc3wBZa9SZF_5_cX+apgH7ysMCMvWmODXk9FeENVg@mail.gmail.com%3E
As the example uses CODI ViewAccessScoped it fails on glassfish4, to fix use CODI 1.0.7-SNAPSHOT or use apache deltaspike 0.7, it also has ViewAccess Scope. I will update the example soon.
I hope it helps
LikeLike
Great job, spring folks has to migrate to JEE 6. Now spring is just waste
LikeLike
Hi Rafael,
I have tried your example with CarDao extending the BaseDao, it works like a charm.
However, from the CarDao class, my NetBeans underlined the class name “CarDao” with the
error message “A session bean must not extend another session bean.” But I can compile,
deploy and run the application without any problem.
I have also heard that a session bean cannot extend another session bean, but why it works here?
I am using NetBeans 8.0.1 and WebLogic 12c for this code testing.
Thanks
LikeLiked by 1 person
Hi Wang, that is not allowed per spec(EJB Core Specification 4.6.2) but implementers usually don’t care about that 😉
There is a new project with ‘generic dao’ here: https://github.com/rmpestano/cdi-crud, in this project i dont use inheritance of session beans.
I hope it helps.
LikeLike
Hi, good explanation, i have tried this as you mentioned and getting below error. Any help would be appreciated.
Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [GenericBaseDao] with qualifiers [@JpaDao] at injection point [[field] @JpaDao @Inject private rest.service.EntitlementsServiceEJB.userDao]
LikeLike
It works! Awesome!.
LikeLiked by 1 person
Hi Rafael! I get this exception while building:
— jaxrs-analyzer-maven-plugin:0.15:analyze-jaxrs (default) @ cdi-crud —
ago 14, 2017 5:20:21 PM org.sonatype.guice.bean.reflect.Logs$JULSink warn
ADVERTENCIA: Error injecting: com.sebastian_daschner.jaxrs_analyzer.maven.JAXRSAnalyzerMojo
com.google.inject.ProvisionException: Guice provision errors:
1) No implementation for org.eclipse.aether.RepositorySystem was bound.
while locating com.sebastian_daschner.jaxrs_analyzer.maven.JAXRSAnalyzerMojo
1 error
at com.google.inject.internal.InjectorImpl$3.get(InjectorImpl.java:974)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1000)
at org.sonatype.guice.bean.reflect.AbstractDeferredClass.get(AbstractDeferredClass.java:45)
at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:84)
at com.google.inject.internal.InternalFactoryToInitializableAdapter.provision(InternalFactoryToInitializableAdapter.java:52)
at com.google.inject.internal.ProviderInternalFactory$1.call(ProviderInternalFactory.java:70)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:100)
at org.sonatype.guice.plexus.lifecycles.PlexusLifecycleManager.onProvision(PlexusLifecycleManager.java:138)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:108)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:55)
at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:68)
at com.google.inject.internal.InternalFactoryToInitializableAdapter.get(InternalFactoryToInitializableAdapter.java:45)
at com.google.inject.internal.InjectorImpl$3$1.call(InjectorImpl.java:965)
at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1011)
at com.google.inject.internal.InjectorImpl$3.get(InjectorImpl.java:961)
at com.google.inject.Scopes$1$1.get(Scopes.java:59)
at org.sonatype.guice.bean.locators.LazyBeanEntry.getValue(LazyBeanEntry.java:83)
at org.sonatype.guice.plexus.locators.LazyPlexusBean.getValue(LazyPlexusBean.java:49)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:253)
at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:245)
at org.apache.maven.plugin.internal.DefaultMavenPluginManager.getConfiguredMojo(DefaultMavenPluginManager.java:455)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:92)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)
Any idea how can I fix this? Thanks in advance, and you did a very nice work here!
LikeLike
Hi Esteban, which maven version are you using? Can you try with 3.3.1 or newer?
LikeLike