MappingException(Attempt to add id) after upgrading to spring boot 2.2.X - spring-data-elasticsearch

After upgrading the spring boot to 2.2.4 (from 2.1.x), org.springframework.cloud:spring-cloud-dependencies to Hoxton.RELEASE and org.springframework.cloud:spring-cloud-stream-dependencies to Horsham.RELEASE.
Getting the following exception when trying to create index document.
Caused by: org.springframework.data.mapping.MappingException: Attempt to add id property private java.util.Map .CatalogIndex.document but already have property private java.lang.String .CatalogIndex.id registered as id. Check your mapping configuration!
Please find the entity class hierarchy. I have removed all the getter and setter for simplicity.
package mypackage.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Parent;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class CatalogIndex {
private static final long serialVersionUID = 1L;
#Id
private String id;
#Parent(type = "Initiative")
private String initiativeId;
private List<Map<String, Object>> typeHierarchy;
private Map<String, Object> document;
private List<Map<String, Object>> filters;
}
package mypackage.entity;
import org.springframework.data.elasticsearch.annotations.Document;
#Document(indexName = "cataloginitiative")
public class CatalogInitiativeIndex extends CatalogIndex { }

Spring Data Elasticsearch, when inspecting the Entity class, tries to figure out which property of the class is to be used as the Id property. A property qualifies for this if one of the following is true:
the property is annotated with #Id
the property is named id
the property is named document
So in your case you have the property id which has a matching name and the annotation, and the property document with a matching name.
You have to rename your property document to somthing different.

Related

CDI will not work with implicit #Dependent scope, unsatisfied injection point compile time error

The container is Glassfish 4.1
I am having a very weird issue with CDI right now. If I don't annotate my NumberGenerator services #Dependent, then I keep getting unsatisfied injection point error when I run the app. But if I explicitly annotate my NumberGenerator implementations, then everything will work. In one word, if I want dependency injection with the #Dependent which is the default scope, I must specify it explicitly.
public interface NumberGenerator {
String generateNumber();
}
The first implementation of NumberGenerator
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
#ThirteenDigits
#Dependent
public class IsbnGenerator implements NumberGenerator {
#Inject
private Logger logger;
#Override
public String generateNumber() {
String isbn = "13-84356-" + Math.abs(new Random().nextInt());
logger.log(Level.INFO, "Generated ISBN : {0}", isbn);
return isbn;
}
}
The second implementation of NumberGenerator
import java.util.Random;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
#EightDigits
#Dependent
public class IssnGenerator implements NumberGenerator {
#Inject
private Logger logger;
#Override
public String generateNumber() {
String issn = "8-" + Math.abs(new Random().nextInt());
logger.info("Generated ISSN : " + issn);
return issn;
}
}
This is where the NumberGenerator will be injected
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.interceptor.Interceptors;
#Dependent
public class BookService {
#Inject
#EightDigits
private NumberGenerator numberGenerator;
public Book createBook(String title, float price, String description) {
Book book = new Book(title, price, description);
book.setNumber(numberGenerator.generateNumber());
return book;
}
}
Finally, the BookService is injected into this JSF managed bean to create a Book instance.
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#Dependent /* if I leave this out, then this bean will not display
the book instance properties on the JSF page, I just see
a blank screen, but when I add this #Dependent annotation
the JSF page displays the dummy content below.
*/
public class MyBean {
#Inject
private BookService bookService;
public Book getBook() {
return bookService.createBook("Dummy Title", 21.05f, "Dummy Description");
}
}
As you can see, I have to use #Dependent for the default scope every time I want DI. Right now, I am injecting the IssnGenerator with the qualifier #EightDigits into the BookService class, and if I remove the #Dependent from the IssnGenerator, I receive this compile error.
Unsatisfied dependencies for type NumberGenerator with qualifiers #EightDigits at injection point [BackedAnnotatedField] #Inject #EightDigits private BookService.numberGenerator
Thank you for any suggestion.
If you do not specify a META-INF/beans.xml file, which seems to be your case, you got an implicit bean archive (see CDI-1.1 §12.1).
In an implicit bean archive, only beans with a bean defining annotation will be discovered by the CDI engine.
Any scope is a bean defining annotation (see CDI-1.1 §2.5). That's why if you add a scope, like #Dependent, you bean gets discovered by CDI and you don't have the unsatisfied dependency error.
The fact that #Dependent is the default scope is not relevant here because without the any scope your beans is simply not discovered.
If you add a META-INF/beans.xml file with the bean-discovery-mode set to all, then you got an explicit bean archive, this means that all beans in your archive will be discovered and will have the #Dependent scope by default.

How to get a node using index

I have an entity Place:
#NodeEntity
#TypeAlias(value="Place")
public class Place implements Serializable{
private static final long serialVersionUID = 1L;
#GraphId
private Long nodeId;
#JsonProperty("id")
#Indexed(unique=true)
private String id;
//...
}
And I try to get a node based on its id attribute this way:
String pfc = "1234";
(Node)template.getIndex(Place.class, "id").get("id", pfc).getSingle()
But I'm having this exception :
java.lang.IllegalStateException: Index name for class java.lang.String id rel: false idx: true must differ from the default name: Place
Must I necessairly add a name to the index?
If yes, how should I do for the existent data?
Which version are you using? In SDN 3.x for Neo4j 2.x the indexes and constraints are used automatically.
I would use a PlaceRepository with a findById() method.
Or in general cypher queries that access place like this:
MATCH (p:Place {id:{id}})-->(x)
RETURN x
You can access them manually with template.merge() or template.findByLabelAndProperty() which I would only recommend when you know what you're doing.

Depedency inject request parameter with CDI and JSF2

When using CDI and JSF2 How can a HTTP request parameter be injected into a bean?
HINT: before reading any further have a look at http://showcase.omnifaces.org/cdi/Param.
Do it yourself is probably obsolete seeing how omnifaces is a de facto standard today. I would probably not have written this if omnifaces had this at the time
CDI does not solve specialized problems like injecting a request parameter. That's supposed to be solved by extensions.
This is already provided by solder. http://docs.jboss.org/seam/3/solder/latest/reference/en-US/html/injectablerefs.html
It will probably be included in Deltaspike 0.4-incubating or similar as well.
That said the code required is rather simple to implement yourself. Example below:
Annotation to use for the injection point (For example private String myParam;)
import javax.enterprise.util.Nonbinding;
import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier
#Retention(RUNTIME)
#Target({METHOD, FIELD, PARAMETER })
public #interface RequestParam {
#Nonbinding
public String value() default "";
}
Now we have the annotation but we can't just ask the container to dependency inject a #RequestParam - we obviously need an implementation.
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
public class RequestParamProducer implements Serializable {
private static final long serialVersionUID = -4260202951977249652L;
#Inject
FacesContext facesContext;
// Producer for #RequestParam
#Produces
#RequestParam
String getRequestParameter(InjectionPoint ip) {
String name = ip.getAnnotated().getAnnotation(RequestParam.class)
.value();
if ("".equals(name))
name = ip.getMember().getName();
return facesContext.getExternalContext().getRequestParameterMap()
.get(name);
}
}
So how does it work? Well quite simply it first checks if you did specify what parameter you wanted as in #Requestparam("longAndTerribleFieldNameBestToSpecify");
If you didn't it will use the fieldName. So if you annoted a setter called setMyInstance it will look for a parameter called setMyInstance.
The normal use case would be to have a String variable that is named exactly like the parameter you want.
Note that we inject FacesContext, that must also be produced. A FacesContext producer could look like this:
class FacesContextProducer {
#Produces #RequestScoped FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
}
End usage:
#Inject
#RequestParam
private String session_secret;
Note that this will not work for Servlet or similar as it requires access to FacesContext. In those cases one need to wrap the injection with for example a bean that is #RequesScoped. You inject that bean instead.

WELD-001408 Unsatisfied dependencies when injecting EntityManager

I have #Stateless bean which implements two interfaces (remote and local). I have also added #LocalBean anotation for accessing bean with a no-interface view.
#Stateless
#LocalBean
public class WeatherDataBean implements WeatherDataBeanRemote, WeatherDataBeanLocal {
#Inject
private EntityManager entityManager;
public WeatherDataBean () {
}
// ....attributes, getter & setter methods ....
}
I use #Inject for this reason taken from this example of JBoss AS7 quickstart:
We use the "resource producer" pattern, from CDI, to "alias" the old fashioned #PersistenceContext injection of the entity manager to a CDI style injection. This allows us to use a consistent injection style (#Inject) throughout the application.
Now previously I have used:
#PersistenceContext(unitName="WeatherStationJPA")
private EntityManager entityManager;
In EJB and it works without any problem. But with #Inject annotation I get this error:
WELD-001408 Unsatisfied dependencies for type [EntityManager] with
qualifiers [#Default] at injection point [[field] #Inject private
ejb.WeatherDataBean.entityManager]
Here is how I have class Resources defined:
public class Resources {
#SuppressWarnings("unused")
#PersistenceContext(unitName="WeatherStationJPA")
#Produces
private EntityManager entityManager;
#Produces
FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
}
Why Is that I get this error if I try to inject entity manager?
EDIT:
On request from #LightGuard I am adding packages that I am using to reference annotations:
WeatherDataBean has:
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Inject;
Resources has:
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;
import javax.inject.Singleton;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
I just had the same problem, I fixed this by adding this class to my project
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
/**
* This class uses CDI to alias Java EE resources, such as the persistence context, to CDI beans
*
* <p>
* Example injection on a managed bean field:
* </p>
*
* <pre>
* #Inject
* private EntityManager em;
* </pre>
*/
public class Resources {
// use #SuppressWarnings to tell IDE to ignore warnings about field not being referenced directly
#SuppressWarnings("unused")
#Produces
#PersistenceContext
private EntityManager em;
// #Produces
// public Logger produceLog(InjectionPoint injectionPoint) {
// return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
// }
#Produces
#RequestScoped
public FacesContext produceFacesContext() {
return FacesContext.getCurrentInstance();
}
}
I have ran into a similar error, turned out I didn't put a beans.xml file in the WEB-INF folder. The beans.xml file can be an empty file sitting in the WEB-INF folder. JBossAS checks that file to start the CDI service.

toString() in Grails Java Domain Class Causes

By default, grails seems to return <class name>:<id> for toString() of an Java domain object. That's not at all what I want of course, so I tried to #Override
the toString() to return what I want. When I tried grails generate-all Tagtype, I got the following error:
java.lang.LinkageError: loader constraint violation: loader (instance of <bootloader>) previously initiated loading for a differen
t type with name "org/w3c/dom/NamedNodeMap"
My code is below. Any help would be greatly appreciated.
#Entity
#Table(name = "tagtype", catalog = "tigger")
#SuppressWarnings("serial")
public class Tagtype implements Serializable {
/**
* Attribute id.
*/
private Integer id;
/**
* Attribute tagtype.
*/
private String tagtype;
/**
* Attribute regexpression
*/
private Regexpression regexpression;
. . .
#Override public String toString() {
StringBuilder result = new StringBuilder();
result.append(this.tagtype);
return result.toString();
}
}
I've overriden toString() in Grails domain classes without any problems, so that can't be the reason. This blog suggests it could be a result of name collisions, either temporary (have you tried running "grails clean"?) or perhaps your class name Tagtype collides with some grails internals.
Another thing you could try is using different versions of Grails, especially the latest 1.1.1 if you aren't already using that. This ML post describes an identical error message that was apparently version dependant.

Resources