How to transfer an array list of objects over the wire through GWTP action - gwt-platform

I am trying to create an action in which the server needs to response an array list of objects over the wire to the client through GWTP Action.
Category class
package com.business.share;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
public class Category implements Serializable{
Long id;
protected String name;
protected String description;
protected boolean status;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean getStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
GetCategories class
package com.business.client.action;
import java.util.ArrayList;
import com.gwtplatform.dispatch.shared.ActionImpl;
import com.business.client.action.GetCategoriesResult;
import com.business.share.Category;
public class GetCategories extends ActionImpl<GetCategoriesResult> {
private ArrayList<Category> categories;
#SuppressWarnings("unused")
public GetCategories() {
// For serialization only
}
public GetCategories(ArrayList<Category> categories) {
this.categories = categories;
}
public ArrayList<Category> getCategories() {
return categories;
}
}
GetCategoriesResult class
package com.business.client.action;
import java.util.ArrayList;
import com.gwtplatform.dispatch.shared.Result;
import com.business.share.Category;
public class GetCategoriesResult implements Result {
private ArrayList<Category> categories;
#SuppressWarnings("unused")
private GetCategoriesResult() {
// For serialization only
}
public GetCategoriesResult(ArrayList<Category> categories) {
this.categories = categories;
}
public ArrayList<Category> getCategories() {
return categories;
}
}
GetCategoriesActionHandler class
package com.business.server.handler;
import java.util.ArrayList;
import com.gwtplatform.dispatch.server.actionhandler.ActionHandler;
import com.business.client.action.GetCategories;
import com.business.client.action.GetCategoriesResult;
import com.business.share.Category;
import com.google.inject.Inject;
import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;
import com.gwtplatform.dispatch.server.ExecutionContext;
import com.gwtplatform.dispatch.shared.ActionException;
public class GetCategoriesActionHandler implements
ActionHandler<GetCategories, GetCategoriesResult> {
#Inject
public GetCategoriesActionHandler() {
}
#Override
public GetCategoriesResult execute(GetCategories action,
ExecutionContext context) throws ActionException {
ArrayList<Category> categories = new ArrayList<Category>();
// dummy data
Category cat1 = new Category();
cat1.setName("cat1");
cat1.setDescription("cat1 desc");
cat1.setStatus(true);
Category cat2 = new Category();
cat1.setName("cat2");
cat1.setDescription("cat2 desc");
cat1.setStatus(false);
categories.add(cat1);
categories.add(cat2);
return new GetCategoriesResult(categories);
}
#Override
public void undo(GetCategories action, GetCategoriesResult result,
ExecutionContext context) throws ActionException {
}
#Override
public Class<GetCategories> getActionType() {
return GetCategories.class;
}
}
And this is a piece of code in CategoryPresenter, which sends async to server.
#Override
protected void onReset() {
super.onReset();
GetCategories getCategoriesAction = new GetCategories();
dispatchAsync.execute(getCategoriesAction, getCategoriesCallback);
}
private final AsyncCallback<GetCategoriesResult> getCategoriesCallback =
new AsyncCallback<GetCategoriesResult>() {
#Override
public void onFailure(Throwable caught) {
}
#Override
public void onSuccess(GetCategoriesResult result) {
getView().getCategoryListBox().clear();
ArrayList<Category> categories = result.getCategories();
for(Category category : categories) {
getView().getCategoryListBox().addItem(category.getName());
}
}
};
I don't know what wrong with this piece of code, but GWT compiler always gives error like this.
Compiling module com.business.Business
Validating newly compiled units
Ignored 3 units with compilation errors in first pass.
Compile with -strict or with -logLevel set to TRACE or DEBUG to see all errors.
Finding entry point classes
[ERROR] Errors in 'file:/.blah..blah..blah../businessapp/src/com/business/client/presenter/CategoryPresenter.java'
[ERROR] Line 75: No source code is available for type com.business.share.Category; did you forget to inherit a required module?
[ERROR] Errors in 'file:/.blah..blah..blah../businessapp/src/com/business/client/action/GetCategoriesResult.java'
[ERROR] Line 11: No source code is available for type com.business.share.Category; did you forget to inherit a required module?
[ERROR] Unable to find type 'com.business.client.Business'
[ERROR] Hint: Previous compiler errors may have made this type unavailable
[ERROR] Hint: Check the inheritance chain from your module; it may not be inheriting a required module or a module may not be adding its source path entries properly
Following this error message, it means, com.business.share.Category is not found, but this file is physically stored in that package already. I don't understand why GWT could not find it. I noticed anywhere that I make call Category class, it brings this error always.
Somebody's got an idea on what's going on?
[EDIT]
The problem is solved.
In my Business.gwt.xml, I have
<source path='shared'/>
But my share package is com.business.share (without d)
I just rename the package name from share to shared.

Try to add an empty constructor to the Category class.

Related

How compile Groovy source but not from filesystem

I compile single groovy source module "in fly" using GroovyClassLoader.parseClass(src) and all is ok.
But problem is when this source module imports other classes, these are not compiled yet. Traditional compiling when I start compilation of one source but other are required and ready on source path, are compiled too.
How can I use GroovyClassLoader with target to compile all other required sources NOT FROM FILESYSYSTEM.
My sources are for example in database, remote http via URI etc.
The key is to make custom URL handling -- you have to implement a URLStreamHandler and a URLConnection.
If you google around, there's some good documentation on how to implement the stream handler and connection classes -- but for what you're doing, you really only need dummy implementations.
Here's some source code to bootstrap you -- it demonstrates how to connect the pieces up. If you provide some implementation of lookupScriptCodeWithJDBCorWhatever you'll be good to go.
import groovy.lang.GroovyResourceLoader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
public class CustomGroovyResourceLoader implements GroovyResourceLoader {
private final GroovyResourceLoader parentLoader;
public CustomGroovyResourceLoader(GroovyResourceLoader parentLoader) {
this.parentLoader = parentLoader;
}
#Override
public URL loadGroovySource(String filename) throws MalformedURLException {
URL resourceURL = null;
if (parentLoader != null) {
resourceURL = parentLoader.loadGroovySource(filename);
}
if (resourceURL == null) {
resourceURL = createURL(filename);
}
return resourceURL;
}
public URL createURL(String resourceName) throws MalformedURLException {
String scriptSourceCode = lookupScriptCodeWithJDBCorWhatever(resourceName);
return new URL(null, "groovy:///" + resourceName,
new GroovyResourceStreamHandler(scriptSourceCode));
}
private String lookupScriptCodeWithJDBCorWhatever(String resourceName) {
//TODO: blah blah blah
return null;
}
class GroovyResourceConnection extends URLConnection {
private final String urlData;
protected GroovyResourceConnection(URL url, String logic) {
super(url);
this.urlData = logic;
}
#Override
public void connect() throws IOException {}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(urlData.getBytes());
}
}
class GroovyResourceStreamHandler extends URLStreamHandler {
private final String scriptSource;
public GroovyResourceStreamHandler(String scriptSource) {
this.scriptSource = scriptSource;
}
#Override
protected URLConnection openConnection(URL u) throws IOException {
GroovyResourceConnection connection = new GroovyResourceConnection(u, scriptSource);
return connection;
}
}
}
You then install this thing with some code that looks like this:
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
groovyClassLoader.setResourceLoader( new CustomGroovyResourceLoader( groovyClassLoader.getResourceLoader() ) );

FacesContextListener implementation

In an attempt to implement the FacesContextListener, which seems to be the ideal place for the current challenge we are facing, I still struggle with its implementation. Attempts to declare it in the faces-config.xml or a construction similar to the ApplicationListener failed (as I probably reference things wrong (except for the class itself of course)).
Can someone provide directions / a short example regarding the implementation of the FacesContextListener?
Create a Java class which implements FacesContextListener interface.
package ch.hasselba.xpages;
import javax.faces.context.FacesContext;
import com.ibm.xsp.event.FacesContextListener;
public class MyFacesContextListener implements FacesContextListener {
public void beforeContextReleased(FacesContext fc) {
System.out.println("beforeContextReleased");
}
public void beforeRenderingPhase(FacesContext fc) {
System.out.println("beforeRenderingPhase");
}
}
Now, add an instance of the class to your XPage:
importPackage( ch.hasselba.xpages )
var fcl = new ch.hasselba.xpages.MyFacesContextListener();
facesContext.addRequestListener( fcl );
Hope this helps!
EDIT:
Here is a Java implementation with an anonymous Listener:
package ch.hasselba.xpages;
import javax.faces.context.FacesContext;
import com.ibm.xsp.context.FacesContextExImpl;
import com.ibm.xsp.event.FacesContextListener;
public class MyObject {
private transient FacesContextListener mFCListener;
public MyObject() {
mFCListener = new FacesContextListener() {
public void beforeContextReleased(FacesContext fc) {
System.out.println("Before Releasing.");
}
public void beforeRenderingPhase(FacesContext fc) {
System.out.println("Before Rendering.");
}
};
FacesContextExImpl fc = (FacesContextExImpl) FacesContext.getCurrentInstance();
fc.addRequestListener( this.mFCListener );
}
}

Creating a ToolTip Managed bean

This is a followup to my previous post at ToolTip Performance in XPages I have got the code to do it written (not tested) so I can't seem to get my Managed Bean to get called properly. My config contians the following:
<managed-bean id="ToolTip">
<managed-bean-name>WFSToolTip</managed-bean-name>
<managed-bean-class>ca.workflo.wfsToolTip.ToolTipText</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
and I have stripped my code down to the bare minimum:
package ca.workflo.wfsToolTip;
public class ToolTipText {
public String getToolTipText(String key){
return key;
}
}
My class is in the build path. I have a simple XPage with one filed on it and a tool tip for that field. The code for the tooltip is:
<xe:tooltip id="tooltip1" for="inputText1">
<xe:this.label>
<![CDATA[#{javascript:WFSToolTip.getToolTipText("More Stuff");}]]>
</xe:this.label>
</xe:tooltip>
When I load the test XPage in the browser I get an error that:
Error while executing JavaScript computed expression
Script interpreter error, line=1, col=12: Error calling method 'getToolTipText(string)' on java class 'ca.workflo.wfsToolTip.ToolTipText'
JavaScript code
1: WFSToolTip.getToolTipText("More Stuff");
I can't figure out why the call to getToolTipText would fail.
Can anyone see where I'm going wrong. This is my first Managed Bean and at the moment it is managing me rather than the other way around.
Thanks.
You need to:
- implement Serializable which boils down to state it and provide a version
- implement Map ... a little more work
Then you use Expression Language instead of SSJS. It would look like #{WFSToolTip["More Stuff"]}
This is how such a class would look like. You need to:
adjust the view name to reflect the name you want
the view needs to be flat, column 1 = tooltip name, column 2 = tooltip text
somewhere (on an admin/config page) you need to call WFSToolTip.clear(); (in SSJS) after you update the values in the configuration.
The example doesn't lazyload since running though a view navigator once is really fast. No point to do all these lookups.
Here you go:
package com.notessensei.xpages;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import lotus.domino.Base;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class Parameters implements Serializable, Map<String, String> {
private final static String CONFIG_VIEW = "keywords";
private static final long serialVersionUID = 1L;
private final Map<String, String> internalMap = new HashMap<String, String>();
public Parameters() {
this.populateParameters(internalMap);
}
private void populateParameters(Map<String, String> theMap) {
Database d = ExtLibUtil.getCurrentDatabase();
try {
View v = d.getView(CONFIG_VIEW);
ViewEntryCollection vec = v.getAllEntries();
ViewEntry ve = vec.getFirstEntry();
ViewEntry nextVe = null;
while (ve != null) {
nextVe = vec.getNextEntry(ve);
// Load the parameters, column 0 is the key, column 0 the value
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
// Cleanup
this.shred(ve);
ve = nextVe;
}
// recycle, but not the current database!!!
this.shred(ve, nextVe, vec, v);
} catch (NotesException e) {
e.printStackTrace();
}
}
public void clear() {
this.internalMap.clear();
this.populateParameters(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
return this.internalMap.get(key);
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
private void shred(Base... morituri) {
for (Base obsoleteObject : morituri) {
if (obsoleteObject != null) {
try {
obsoleteObject.recycle();
} catch (NotesException e) {
// We don't care we want go get
// rid of it anyway
} finally {
obsoleteObject = null;
}
}
}
}
}
The difference to a regular HashMap is only the constructor that populates it. Hope that clarifies it.
I've never seen that id property.. My beans in faces-config look like this:
<managed-bean>
<managed-bean-name>CurrentJob</managed-bean-name>
<managed-bean-class>com.domain.inventory.Job</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
Technically managed beans should implement Serializable and have a blank constructor. So you should have something like this inside :
public ToolTipText() {
}
I THINK you can get away without the Serializable for somethings... I always implement though but I'm sure you need the no argument constructor.
Thanks to all that have responded and helped out here especially Stephan Wissel. I thought I would post my version of Stephan's code, pretty much the same. There are issues with making the class an ApplicationScope because you need to shut down the HTTP task to refresh and reload the Class. What I did was added a button to the custom control where I to the view of the tooltips where I do the CRUD stuff and in the button do WFSToolTip().clear() and it rebuilds the map. Pretty neat. My next task for this is try to do the CRUD using JAVA and update the map directly. At the moment though I need to move on to my next task.
My next task revolves around a very similar Class. I have a master database that contains all the basic design and code. Then I have one or more applications that use that code and store the documents in their own database that contains the forms and views for that specific application. In the master I have created one or more application documents. Each of these documents contains the AppName (the key value) then the Map value is an array (Vector) containing the ReplicaID of the Application Database and a few other pieces of information. My class the loads a Map entry for each Application and collects a bunch of other information about the application from several places and stores that in the Map Value. At this point then I can set Database db = thisClass.getDatabase("App Name"). so a single custom control can be used for any/all of the applications. Pretty cool. I think I could get to like this.
Anyway here is the code I'm using for the ToolTips - BTW It has taken an XPage with about 175 fields and 100+ tooltips from being painfully slow to being acceptable. The good thing about it is that the XPage is creating a process profile document and once created it is not frequently modified as an admin action - not an everyday user action.
Please feel free point out error, omitions or suggestions to the code:
package ca.workflo.wfsToolTip;
import lotus.domino.Base;
import lotus.domino.Session;
import lotus.domino.Database;
import lotus.domino.View;
import lotus.domino.NotesException;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class ToolTipText implements Serializable, Map<String, String> {
private static final long serialVersionUID = 1L;
private Session s;
private String repID;
private Database db;
private Database helpDB;
private View helpView;
private ViewEntry ve;
private ViewEntry tVE;
private ViewEntryCollection veCol;
private final Map<String, String> internalMap = new HashMap<String, String>();
public ToolTipText() {
this.populateMap(internalMap);
}
private void populateMap(Map<String, String> theMap) {
try {
s = ExtLibUtil.getCurrentSession();
db = s.getCurrentDatabase();
repID = db.getProfileDocument("frmConfigProfile", "").getItemValue(
"WFSHelpRepID").firstElement().toString();
helpDB = s.getDbDirectory(null).openDatabaseByReplicaID(repID);
helpView = helpDB.getView("vwWFSToolTipHelp");
veCol = helpView.getAllEntries();
ve = veCol.getFirstEntry();
ViewEntry tVE = null;
while (ve != null) {
tVE = veCol.getNextEntry(ve);
Vector colVal = ve.getColumnValues();
theMap.put(colVal.get(0).toString(), colVal.get(1).toString());
recycleObjects(ve);
ve = tVE;
}
} catch (NotesException e) {
System.out.println(e.toString());
}finally{
recycleObjects(ve, tVE, veCol, helpView, helpDB);
}
}
public void clear() {
this.internalMap.clear();
this.populateMap(this.internalMap);
}
public boolean containsKey(Object key) {
return this.internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return this.internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return this.internalMap.entrySet();
}
public String get(Object key) {
try {
if (this.internalMap.containsKey(key)) {
return this.internalMap.get(key);
} else {
return "There is no Tooltip Help for " + key;
}
} catch (Exception e) {
return "error in tooltip get Object ";
}
}
public boolean isEmpty() {
return this.internalMap.isEmpty();
}
public Set<String> keySet() {
return this.internalMap.keySet();
}
public String put(String key, String value) {
return this.internalMap.put(key, value);
}
public void putAll(Map<? extends String, ? extends String> m) {
this.internalMap.putAll(m);
}
public String remove(Object key) {
return this.internalMap.remove(key);
}
public int size() {
return this.internalMap.size();
}
public Collection<String> values() {
return this.internalMap.values();
}
public static void recycleObjects(Object... args) {
for (Object o : args) {
if (o != null) {
if (o instanceof Base) {
try {
((Base) o).recycle();
} catch (Throwable t) {
// who cares?
}
}
}
}
}
}

JaxB XmlSeeAlso, XmlElementRef, newInstance with Classes

The answer to my question
JaxB reference resolving
led me to further pursue the details of the issue regarding the use of XmlSeeAlso, XmlElementReference and the specification of classes involved in JaxbContext.newInstance.
I started with trying to answer the question:
Is it possible to #XmlSeeAlso on a class without no-args constructor which has #XmlJavaTypeAdapter?
i created the Junit test below. To do so I came accross:
#XmlJavaTypeAdapter w/ Inheritance
#XmlSeeAlso alternative
In this state the code is compilable and runs - it marshals o.k. but does not umarshal as expected.
The marshal result is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Message>
<BasicInfo ref="id001"/>
</Message>
Unmarshalling does not create a BasicInfo as would be expected from the code in the Adapter:
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
I find this all very confusing - things seem to be somewhat contradictory and mostly do not work as I'd expect regarding the JaxB settings involved. Most combinations do not work, those that do work do not give the full result yet. I assume that is also depending on the version and implementation being used.
What nees to be done to get this working?
What is a minimal set of XmlElementRef,XmlSeeAlso and JaxbContext newInstance referencing of the necessary classes?
How can I check which JaxB Implementation is being used? I am using EclipseLink 2.3.2? and I'm not sure whether this uses MoxY or not.
Update:
after consideration of:
JAXB xsi:type subclass unmarshalling not working
http://www.java.net/node/654579
http://jaxb.java.net/faq/#jaxb_version
http://www.eclipse.org/eclipselink/documentation/2.4/moxy/type_level003.htm
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?
The modified code below is close to what is intended.
Errors that showed up while trying were:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "com.bitplan.storage.jaxb.TestRefId$BasicInfo$BasicInfoRef" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
The code has quite a few comments which can be commented in and out to find out what happens if one tries ...
I still have no clue what the optimal solution would be to avoid superfluous annotations.
modified code:
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
import org.junit.Test;
/**
* Test the Reference/Id handling for Jaxb
*
* #author wf
*
*/
public class TestRefId {
#XmlDiscriminatorNode("#reforid")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapter(RefOrId.Adapter.class)
public static class RefOrId {
#XmlAttribute(name = "reforid")
public String reforid;
public RefOrId() {
}
#XmlAttribute
public String id;
#XmlAttribute
public String ref;
public static class Adapter extends XmlAdapter<RefOrId, RefOrId> {
#Override
public RefOrId marshal(RefOrId idref) throws Exception {
if (idref == null)
return null;
System.out.println("marshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
if (idref instanceof Ref)
return new Id(((Ref) idref).ref);
return idref;
}
#Override
public RefOrId unmarshal(RefOrId idref) throws Exception {
System.out.println("unmarshalling " + idref.getClass().getSimpleName()
+ " reforid:" + idref.reforid);
return idref;
}
}
}
#XmlDiscriminatorValue("id")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Id extends RefOrId {
public Id() {
reforid = "id";
}
public Id(String pId) {
this();
this.id = pId;
}
}
#XmlDiscriminatorValue("ref")
#XmlAccessorType(XmlAccessType.FIELD)
public static class Ref extends RefOrId {
public Ref() {
reforid = "ref";
}
public Ref(String pRef) {
this();
this.ref = pRef;
}
}
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a
* -class-without-no-args-constructor-which-has
*/
#XmlRootElement(name = "BasicInfo")
public static class BasicInfo {
public BasicInfo() {
};
public BasicInfo(String pId, String pInfo) {
this.id = new Id(pId);
this.basic = pInfo;
}
// #XmlTransient
public RefOrId id;
public String basic;
}
#XmlRootElement(name = "SpecificInfoA")
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public SpecificInfoA() {
};
public SpecificInfoA(String pId, String pInfo, String pInfoA) {
super(pId, pInfo);
infoA = pInfoA;
}
public String infoA;
}
#XmlRootElement(name = "Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({ SpecificInfoA.class, BasicInfo.class })
public static class Message {
public Message() {
};
public Message(BasicInfo pBasicInfo) {
basicInfos.add(pBasicInfo);
}
// #XmlJavaTypeAdapter(BasicInfo.Adapter.class)
// #XmlElement(name="basicInfo",type=BasicInfoRef.class)
#XmlElementWrapper(name = "infos")
#XmlElement(name = "info")
public List<BasicInfo> basicInfos = new ArrayList<BasicInfo>();
}
/**
* marshal the given message
*
* #param jaxbContext
* #param message
* #return - the xml string
* #throws JAXBException
*/
public String marshalMessage(JAXBContext jaxbContext, Message message)
throws JAXBException {
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml = sw.toString();
return xml;
}
/**
* test marshalling and umarshalling a message
*
* #param message
* #throws JAXBException
*/
public void testMessage(Message message) throws JAXBException {
#SuppressWarnings("rawtypes")
Class[] classes = { Message.class, SpecificInfoA.class, BasicInfo.class }; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
// https://stackoverflow.com/questions/11966714/xmljavatypeadapter-not-being-detected
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
String xml = marshalMessage(jaxbContext, message);
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull(message.basicInfos);
for (BasicInfo binfo : message.basicInfos) {
RefOrId id = binfo.id;
assertNotNull(id);
System.out.println("basicInfo-id " + id.getClass().getSimpleName()
+ " for reforid " + id.reforid);
// assertEquals(message.basicInfo.id, result.basicInfo.id);
}
xml = marshalMessage(jaxbContext, result);
System.out.println(xml);
}
/**
* switch Moxy
*
* #param on
* #throws IOException
*/
public void switchMoxy(boolean on) throws IOException {
File moxySwitch = new File(
"src/test/java/com/bitplan/storage/jaxb/jaxb.properties");
if (on) {
PrintWriter pw = new PrintWriter(new FileWriter(moxySwitch));
pw.println("javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory");
pw.close();
} else {
moxySwitch.delete();
}
}
#Test
public void testStackOverflow8292427() throws JAXBException, IOException {
boolean[] moxyOnList = { false };
for (boolean moxyOn : moxyOnList) {
switchMoxy(moxyOn);
System.out.println("Moxy used: " + moxyOn);
Message message = new Message(new BasicInfo("basicId001",
"basicValue for basic Info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
message.basicInfos.add(new SpecificInfoA("specificId002",
"basicValue for specific Info", "specific info"));
testMessage(message);
}
}
}
This is the Junit Test as mentioned above (before update):
package com.bitplan.storage.jaxb;
import static org.junit.Assert.*;
import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;
import org.junit.Test;
// Test the Reference/Id handling for Jaxb
public class TestRefId {
/*
* https://stackoverflow.com/questions/8292427/is-it-possible-to-xmlseealso-on-a-class-without-no-args-constructor-which-has
*/
#XmlRootElement(name="BasicInfo")
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class BasicInfo {
#XmlRootElement(name="BasicInfo")
#XmlAccessorType(XmlAccessType.FIELD)
public static class BasicInfoRef {
#XmlAttribute(name = "ref")
public String ref;
}
public static class Adapter extends XmlAdapter<BasicInfoRef,BasicInfo>{
#Override
public BasicInfoRef marshal(BasicInfo info) throws Exception {
BasicInfoRef infoRef = new BasicInfoRef();
infoRef.ref=info.id;
return infoRef;
}
#Override
public BasicInfo unmarshal(BasicInfoRef info) throws Exception {
BasicInfo binfo=new BasicInfo();
binfo.id=info.ref;
return binfo;
}
} // Adapter
public String id;
public String basic;
}
#XmlJavaTypeAdapter(BasicInfo.Adapter.class)
public static class SpecificInfoA extends BasicInfo {
public String infoA;
}
#XmlRootElement(name="Message")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({SpecificInfoA.class,BasicInfo.class})
public static class Message {
// https://stackoverflow.com/questions/3107548/xmljavatypeadapter-w-inheritance
#XmlElementRefs({
#XmlElementRef(name="basic", type=BasicInfo.class),
// #XmlElementRef(name="infoA", type=SpecificInfoA.class)
})
public BasicInfo basicInfo;
}
#Test
public void testStackOverflow8292427() throws JAXBException {
Message message=new Message();
SpecificInfoA info=new SpecificInfoA();
info.id="id001";
info.basic="basicValue";
info.infoA="infoAValue";
message.basicInfo=info;
#SuppressWarnings("rawtypes")
Class[] classes= {Message.class,SpecificInfoA.class,BasicInfo.class,BasicInfo.BasicInfoRef.class}; // BasicInfo.class,};
// https://stackoverflow.com/questions/8318231/xmlseealso-alternative/8318490#8318490
JAXBContext jaxbContext = JAXBContext.newInstance(classes);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(message, sw);
String xml=sw.toString();
System.out.println(xml);
Unmarshaller u = jaxbContext.createUnmarshaller();
Message result = (Message) u.unmarshal(new StringReader(xml));
assertNotNull(result);
assertNotNull("basicInfo should not be null",result.basicInfo);
assertEquals("id001",result.basicInfo.id);
}
}

JAXB issue-- unexpected element

My JAXB parser suddenly stopped working today. It was working for several weeks. I get the following message. I haven't changed this code for several weeks. Wondering if this set up is good.
EDIT 2: Please could somebody help me! I can't figure this out.
EDIT 1:
My acceptance tests running the same code below are working fine. I believe this is a
classloading issue. I am using the JAXB and StAX in the JDK. However, when I deploy to jboss 5.1, I get the error below. Using 1.6.0_26 (locally) and 1.6.0_30 (dev server). Still puzzling over a solution.
unexpected element (uri:"", local:"lineEquipmentRecord"). Expected
elements are
<{}switchType>,<{}leSwitchId>,<{}nodeAddress>,<{}leId>,<{}telephoneSuffix>,<{}leFormatCode>,<{}groupIdentifier>,<{}telephoneNpa>,<{}telephoneLine>,<{}telephoneNxx>
Here is my unmarshalling class:
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
public class PartialUnmarshaller<T> {
XMLStreamReader reader;
Class<T> clazz;
Unmarshaller unmarshaller;
public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException {
this.clazz = clazz;
this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
/* ignore headers */
skipElements(XMLStreamConstants.START_DOCUMENT);
/* ignore root element */
reader.nextTag();
/* if there's no tag, ignore root element's end */
skipElements(XMLStreamConstants.END_ELEMENT);
}
public T next() throws XMLStreamException, JAXBException {
if (!hasNext())
throw new NoSuchElementException();
T value = unmarshaller.unmarshal(reader, clazz).getValue();
skipElements(XMLStreamConstants.CHARACTERS, XMLStreamConstants.END_ELEMENT);
return value;
}
public boolean hasNext() throws XMLStreamException {
return reader.hasNext();
}
public void close() throws XMLStreamException {
reader.close();
}
private void skipElements(Integer... elements) throws XMLStreamException {
int eventType = reader.getEventType();
List<Integer> types = new ArrayList<Integer>(Arrays.asList(elements));
while (types.contains(eventType))
eventType = reader.next();
}
}
This class is used as follows:
List<MyClass> lenList = new ArrayList<MyClass>();
PartialUnmarshaller<MyClass> pu = new PartialUnmarshaller<MyClass>(
is, MyClass.class);
while (pu.hasNext()) {
lenList.add(pu.next());
}
The XML being unmarshalled:
<?xml version="1.0" encoding="UTF-8"?>
<lineEquipment>
<lineEquipmentRecord>
<telephoneNpa>333</telephoneNpa>
<telephoneNxx>333</telephoneNxx>
<telephoneLine>4444</telephoneLine>
<telephoneSuffix>1</telephoneSuffix>
<nodeAddress>xxxx</nodeAddress>
<groupIdentifier>LEN</groupIdentifier>
</lineEquipmentRecord>
<lineEquipmentRecord>
<telephoneNpa>111</telephoneNpa>
<telephoneNxx>111</telephoneNxx>
<telephoneLine>2222</telephoneLine>
<telephoneSuffix>0</telephoneSuffix>
<nodeAddress>xxxx</nodeAddress>
<groupIdentifier>LEN</groupIdentifier>
</lineEquipmentRecord>
</lineEquipment>
Finally, here is MyClass:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* This class is used as an envelope to hold Martens
* line equipment information.
* #author spgezf
*
*/
#XmlRootElement(name="lineEquipmentRecord")
public class MyClass {
private String telephoneNpa;
private String telephoneNxx;
private String telephoneLine;
private String telephoneSuffix;
private String nodeAddress;
private String groupIdentifier;
public MyClass(){
}
// Getters and Setters.
#XmlElement(name="telephoneNpa")
public String getTelephoneNpa() {
return telephoneNpa;
}
public void setTelephoneNpa(String telephoneNpa) {
this.telephoneNpa = telephoneNpa;
}
#XmlElement(name="telephoneNxx")
public String getTelephoneNxx() {
return telephoneNxx;
}
public void setTelephoneNxx(String telephoneNxx) {
this.telephoneNxx = telephoneNxx;
}
#XmlElement(name="telephoneLine")
public String getTelephoneLine() {
return telephoneLine;
}
public void setTelephoneLine(String telephoneLine) {
this.telephoneLine = telephoneLine;
}
#XmlElement(name="telephoneSuffix")
public String getTelephoneSuffix() {
return telephoneSuffix;
}
public void setTelephoneSuffix(String telephoneSuffix) {
this.telephoneSuffix = telephoneSuffix;
}
#XmlElement(name="nodeAddress")
public String getNodeAddress() {
return nodeAddress;
}
public void setNodeAddress(String nodeAddress) {
this.nodeAddress = nodeAddress;
}
#XmlElement(name="groupIdentifier")
public String getGroupIdentifier() {
return groupIdentifier;
}
public void setGroupIdentifier(String groupIdentifier) {
this.groupIdentifier = groupIdentifier;
}
}
Thanks, this is classloading issue I couldn't overcome so I abandoned JAXB.
Your PartialUnmarshaller code worked for me. Below is an alternate version that changes the skipElements method that may work better.
import java.io.InputStream;
import java.util.NoSuchElementException;
import javax.xml.bind.*;
import javax.xml.stream.*;
public class PartialUnmarshaller<T> {
XMLStreamReader reader;
Class<T> clazz;
Unmarshaller unmarshaller;
public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException {
this.clazz = clazz;
this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
/* ignore headers */
skipElements();
/* ignore root element */
reader.nextTag();
/* if there's no tag, ignore root element's end */
skipElements();
}
public T next() throws XMLStreamException, JAXBException {
if (!hasNext())
throw new NoSuchElementException();
T value = unmarshaller.unmarshal(reader, clazz).getValue();
skipElements();
return value;
}
public boolean hasNext() throws XMLStreamException {
return reader.hasNext();
}
public void close() throws XMLStreamException {
reader.close();
}
private void skipElements() throws XMLStreamException {
while(reader.hasNext() && !reader.isStartElement()) {
reader.next();
}
}
}

Resources