JAXB implementation in Java 1.8 is incompatible with java 1.7 Collection instances being persisted now must be the same instances [duplicate] - xml-parsing

The code below used to work under the JAXB implementation used by JDK 1.7, but now under JDK 1.8 it's broken. In the code below you will find the key change that seems to make it work in 1.8. The "fix" under 1.8 is not really a fix because it's bad practice to expose internal collections for direct modification by the outside world. I want to control access to the internal list through my class and I don't want to complicate things by making observable collections and listening to them. This is not acceptable.
Is there any way to get my original code to work under the JAXB of JD 1.8?
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
list.clear();
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
// return new ArrayList(list); // this was the original code that worked under 1.7
return list; //this is the only thing that works under 1.8
}
After more analysis, the problem seems to be coming from JAXB not calling the setter method for collections anymore (it used to under JDK 1.7). Now under JDK 1.8, it calls the getter and modifies the collection directly. This poses several problems:
1-forces the user to expose an internal collection to the outside world for free modification (bad practice)
2-doesn't allow the user to do any custom code when the list changes (such as what you could do if the setter was called). It might be possible to make an observable collection and listen to it, but this is a much more complicated workaround than just calling the setter method.

Background
When a collection property is mapped in JAXB it first checks the getter to see if the collection property has been pre-initialized. In the example below I want to have my property exposed as List<String>, but have the backing implementation be a LinkedList ready to hold 1000 items.
private List<String> foos = new LinkedList<String>(1000);
#XmlElement(name="foo")
public List<String> getFoos() {
return foos;
}
Why Your Code Used to Work
If you previously had JAXB call the setter on a property mapped to a collection that returned a non-null response from the getter, then there was a bug in that JAXB implementation. Your code should not have worked in the previous version either.
How to Get the Setter Called
To have the setter called you just need to have your getter return null, on a new instance of the object. Your code could look something like:
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Foo")
public class Foo {
private List<CustomObject> list = null;
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
if (null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
if (null == list) {
return null;
}
return new ArrayList(list);
}
}
UPDATE
If you don't need to perform any logic on the List returned from JAXB's unmarshalling then using field access may be an acceptable solution.
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
private List<CustomObject> list = null;
public synchronized void setList(List<CustomObject> values) {
if(null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
return new ArrayList(list);
}
}

Related

Spring Data Neo4j 4 and dynamic properties

At my Neo4j/SDN 4 project I have a following entity:
#NodeEntity
public class Value extends BaseEntity {
#Index(unique = false)
private Object value;
private String description;
...
}
During the application run-time I want to be able to add a new dynamic properties to Value node, like for example value_en_US, value_fr_FR.
Right now I don't know what exact properties will be added to a particular Value node during application run-time so I can't define these properties at the code as a separate fields in Value.
Is there at SDN 4 any mechanisms to define these properties during the application run-time? I need something similar to DynamicProperties from SDN 3.
There is no such functionality in SDN 4, but it will be added in SDN 5 through a #Properties annotation on Map.
It will be available for testing in snapshot version very soon.
Check out this commit for more details
You might also want to look at this response to a similar question.
https://stackoverflow.com/a/42632709/5249743
Just beware that in that answer the function:
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(field.getName());
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
is not bullet proof. For one thing it doesn't look at #Property annotations. So if you want to go down that route keep your eyes open.
An 'improvement' is
public void addAllFields(Class<?> type) {
for (Field field : type.getDeclaredFields()) {
blacklist.add(findName(field));
}
if (type.getSuperclass() != null) {
addAllFields(type.getSuperclass());
}
}
private String findName(Field field) {
Property property = field.getAnnotation(Property.class);
if(property == null || "".equals(property.name())) {
return field.getName();
} else {
return property.name();
}
}
But this obviously doesn't look for the annotation on methods...

JAXB Configuration was broken by upgrading from JDK 1.7 to JDK 1.8 u05 for collections

The code below used to work under the JAXB implementation used by JDK 1.7, but now under JDK 1.8 it's broken. In the code below you will find the key change that seems to make it work in 1.8. The "fix" under 1.8 is not really a fix because it's bad practice to expose internal collections for direct modification by the outside world. I want to control access to the internal list through my class and I don't want to complicate things by making observable collections and listening to them. This is not acceptable.
Is there any way to get my original code to work under the JAXB of JD 1.8?
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
list.clear();
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
// return new ArrayList(list); // this was the original code that worked under 1.7
return list; //this is the only thing that works under 1.8
}
After more analysis, the problem seems to be coming from JAXB not calling the setter method for collections anymore (it used to under JDK 1.7). Now under JDK 1.8, it calls the getter and modifies the collection directly. This poses several problems:
1-forces the user to expose an internal collection to the outside world for free modification (bad practice)
2-doesn't allow the user to do any custom code when the list changes (such as what you could do if the setter was called). It might be possible to make an observable collection and listen to it, but this is a much more complicated workaround than just calling the setter method.
Background
When a collection property is mapped in JAXB it first checks the getter to see if the collection property has been pre-initialized. In the example below I want to have my property exposed as List<String>, but have the backing implementation be a LinkedList ready to hold 1000 items.
private List<String> foos = new LinkedList<String>(1000);
#XmlElement(name="foo")
public List<String> getFoos() {
return foos;
}
Why Your Code Used to Work
If you previously had JAXB call the setter on a property mapped to a collection that returned a non-null response from the getter, then there was a bug in that JAXB implementation. Your code should not have worked in the previous version either.
How to Get the Setter Called
To have the setter called you just need to have your getter return null, on a new instance of the object. Your code could look something like:
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Foo")
public class Foo {
private List<CustomObject> list = null;
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
if (null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
if (null == list) {
return null;
}
return new ArrayList(list);
}
}
UPDATE
If you don't need to perform any logic on the List returned from JAXB's unmarshalling then using field access may be an acceptable solution.
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
private List<CustomObject> list = null;
public synchronized void setList(List<CustomObject> values) {
if(null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
return new ArrayList(list);
}
}

How can I update a collection that is #Produces #ApplicationScoped?

I'm currently migrating away from Seam's #Factory annotation. Combined with #Observer, I could do this:
#Factory(value = "optionsList", scope = ScopeType.APPLICATION)
#Observer("entity.modified")
public List<MyBean> produceEntityOptions() {
List l = getEm().createQuery('select e from entity e').getResultList();
Contexts.getApplicationContext().set("optionsList", l);
return l;
}
Which would cache a list of possible options for use in e.g. <f:selectItems> (the actual computation can be more complicated).
I've translated this for use with CDI to
#Produces #Named("optionsList") #ApplicationScoped
public List<MyBean> produceEntityOptions() {
return getEm().createQuery('select e from entity e').getResultList();
}
but this loses the ability to recreate the cache (only) when an external event signals the cache has gone stale. How can I get that back?
Here's what you could do:
#ApplicationScoped
public class MyListProducer {
// the current list
private List<MyBean> listOfBeans;
// resets / reloads/ refreshes list
private void loadList() {
this.listOfBeans = getEm().createQuery('select e from entity e').getResultList();
}
// initialize the list
#PostConstruct
protected void postConstruct() {
loadList();
}
// listen for the stale event - you'll have to create a type (maybe even qualifiers) yourself
private void resetList(#Observes MyCustomListIsStaleEvent evt) {
loadList();
}
// the producer - to ensure that the producer is called after you refresh the list, make the list of scope #Dependent instead of #ApplicationScoped
#Produces #Named("optionsList")
protected List<MyBean> getList() {
return this.listOfBeans;
}
}
I think that in effect, this is what you want. But I don't exclude the possibility that there might be differences - don't know Seam very much.
Side note: You should think about synchronizing the observer and the producer methods, either with plain old synchronization or by making the above a stateful session bean and taking advantage of EJB synchronization mechanisms.

Bind registered name to component instance in structuremap

I am about to switch from Windsor to Structuremap for an existing project with ~100 registered components (mostly singletons).
All components inherit from a common base class that provides logging and health tracking and for this reason, contains a "Name" property used to identify component instances.
With Windsor, it was possible to set the component's Name property to the name that was used to register the component in the IOC container (We used a Facility for this).
My question: Is something like this possible with Structuremap?
(I dream of a call to c.For<IFoo>.Use<Bar>.Named("Doe") that magically results in instanceOfBar.Name = "Doe" somewhere.)
Here is what I tried:
using System;
using StructureMap;
using StructureMap.Interceptors;
using System.Diagnostics;
namespace ConsoleApplication1
{
interface IServiceA { }
interface IServiceB { }
class Base
{
public string Name { get; set; }
}
class ComponentA : Base, IServiceA { }
class ComponentB : Base, IServiceB
{
public ComponentB(IServiceA serviceA)
{
this.ServiceA = serviceA;
}
public IServiceA ServiceA { get; private set; }
}
class SetNameInterceptor : TypeInterceptor
{
public bool MatchesType(Type type) { return true; }
public object Process(object target, IContext context)
{
// *** Any other way? This does not work...
string name = context.BuildStack.Current != null ? context.BuildStack.Current.Name : context.RequestedName;
((Base)target).Name = name;
return target;
}
}
class Program
{
static void Main(string[] args)
{
Container container = new Container(c =>
{
c.RegisterInterceptor(new SetNameInterceptor());
c.For<IServiceA>().Use<ComponentA>().Named("A");
c.For<IServiceB>().Use<ComponentB>().Named("B");
});
var b = container.GetInstance<IServiceB>();
// both Fail:
Debug.Assert(((ComponentB)b).Name == "B");
Debug.Assert(((ComponentA)((ComponentB)b).ServiceA).Name == "A");
}
}
}
The above obviously does not work, I tried several variations but had no luck. The registered name of the target object does not seem to be consistently reachable via IContext.
My second best approach would be to define a new "NamedComponent(...)" extension method that resolves to Named(name).WithProperty(x => x.Name).EqualTo(name), but I wonder if this can be avoided to keep component registration as "structuremap-like" as possible?
Am I missing something?
I've never used WithProperty before but if it works the way I'd expect it should do the trick for you.
I think I would favor using EnrichWith though. Something like:
c.For<IFoo>().Use<Foo>().Named(name).EnrichWith(f => f.Name = name);
EnrichWith is a bit more explicit about what it's doing IMO, and lets you call any code on your instance before returning it to the caller. I like that this lets you do a straightforward assignment as well.
There is also a more complex handler you can use with EnrichWith that gives access to the context of the request - this would allow you to do something like this:
c.For<IFoo>().Use<Foo>().Named(name)
.EnrichWith((c, i) => {
i.Name = c.RequestedName;
return i;
});
This may be overkill for your situation but the contextual awareness can be pretty useful.

Castle Windsor Typed Factory Weirdness

I'm getting some very unexpected (I think) behavior with the Typed Factory facility. Basically, it's reusing transient component instances for constructor injection with Func<T>.
Here is the gist of it:
// this guy is registered as service EntityFrameworkRepositoryProvider, not IRepository
[Transient]
public class EntityFrameworkRepositoryProvider : IRepository
{
public EntityFrameworkRepositoryProvider(ObjectContext objectContext, Assembly assembly)
{
// HOLY MOLY BATMAN!! Every time I hit this constructor when getProvider gets called,
// I don't actually get the arguments passed into getProvider. I get the
// arguments that were passed into getProvider the very FIRST time
// it was called...in other words I get the wrong
// ObjectContext (for the wrong connection string)...BIG PROBLEM
// do stuff...
}
}
[Singleton]
// this guy gets registered on startup
internal class EntityFrameworkRepositoryProviderFactory : IRepositoryProviderFactory
{
private readonly Func<ObjectContext, Assembly, EntityFrameworkRepositoryProvider> getProvider;
public EntityFrameworkRepositoryProviderFactory(Func<ObjectContext, Assembly, EntityFrameworkRepositoryProvider> getProvider)
{
this.getProvider = getProvider;
// do stuff...
}
public IRepository CreateRepository()
{
var provider = getProvider(new ObjectContext(GetConnectionString()),
Assembly.GetExecutingAssembly);
// do stuff...
}
public string GetConnectionString()
{
// returns one of a few diff EF connection strings
}
}
I also have a standard LazyComponentLoader. What am I doing wrong here? What should I do instead?
Thanks.
Problem was this seemingly harmless code in the LazyComponentLoader:
public IRegistration Load(string key, Type service, IDictionary arguments)
{
var component = Component.For(service);
if (!string.IsNullOrEmtpy(key))
{
component = component.Named(key);
}
if (arguments != null)
{
// This makes the typed factory facility screw up. (Merge is an extension method that merges dictionaries)
component.DynamicParameters((k, d) => d.Merge(arguments));
}
return component;
}
public static void Merge(this IDictionary target, IDictionary source)
{
foreach (object key in source.Keys)
{
target[key] = source[key];
}
}

Resources