Read content of repeating sections with apache POI - ms-word

I Have a word document with a repeating section, containing other content controls.
In java project, I have a function that gets all sdts (content controls) from a word document in apache POI, in a List List.
When I inspect my repeating section in that list, I can get the text inside all content controls (inside my repeating section) but is apears as a long paragraph instead of other sdt nodes.
Is there a way to inspect content of repeating section sdt with Apache POI ? I can't find anything about it in the doc
function that gets all sdts
private static List
extractSDTsFromBodyElements(List<IBodyElement> elements) {
List<AbstractXWPFSDT> sdts = new ArrayList<AbstractXWPFSDT>();
for (IBodyElement e : elements) {
if (e instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e;
sdts.add(sdt);
} else if (e instanceof XWPFParagraph) {
XWPFParagraph p = (XWPFParagraph) e;
for (IRunElement e2 : p.getIRuns()) {
if (e2 instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e2;
sdts.add(sdt);
}
}
}
}
return sdts;
}

The XWPF part of apache poi is rudimentary until now and highly in development. In XWPFSDT is this mentioned also: "Experimental class to offer rudimentary read-only processing of of StructuredDocumentTags/ContentControl". So until now your code only gets the surrounding XWPFSDT of the repeating content control but not the inner controls. One could have seen that by having some debugging outputs in the code. See my System.out.println(...).
So to really get all XWPFSDTs we must go other ways using the underlaying XMLdirectly.
Lets have a complete example.
Look at this Worddocument:
As you see there is a single control to input the group name, then a repeating content control around three controls to input name, amount and date and then a single control to input the employee. All controls which shall be read have titles set. So whether the title is set, is the criterion whether a control is important for reading or not.
The following code now can read all controls and their content:
import java.io.FileInputStream;
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.ArrayList;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.apache.xmlbeans.XmlCursor;
import javax.xml.namespace.QName;
public class ReadWordForm {
/*
private static List<AbstractXWPFSDT> extractSDTsFromBodyElements(List<IBodyElement> elements) {
List<AbstractXWPFSDT> sdts = new ArrayList<AbstractXWPFSDT>();
for (IBodyElement e : elements) {
if (e instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e;
System.out.println("block: " + sdt);
sdts.add(sdt);
} else if (e instanceof XWPFParagraph) {
XWPFParagraph p = (XWPFParagraph) e;
for (IRunElement e2 : p.getIRuns()) {
if (e2 instanceof XWPFSDT) {
XWPFSDT sdt = (XWPFSDT) e2;
System.out.println("inline: " + sdt);
sdts.add(sdt);
}
}
}
}
return sdts;
}
*/
private static List<XWPFSDT> extractSDTsFromBody(XWPFDocument document) {
XWPFSDT sdt;
XmlCursor xmlcursor = document.getDocument().getBody().newCursor();
QName qnameSdt = new QName("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "sdt", "w");
List<XWPFSDT> allsdts = new ArrayList<XWPFSDT>();
while (xmlcursor.hasNextToken()) {
XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
if (tokentype.isStart()) {
if (qnameSdt.equals(xmlcursor.getName())) {
if (xmlcursor.getObject() instanceof CTSdtRun) {
sdt = new XWPFSDT((CTSdtRun)xmlcursor.getObject(), document);
//System.out.println("inline: " + sdt);
allsdts.add(sdt);
} else if (xmlcursor.getObject() instanceof CTSdtBlock) {
sdt = new XWPFSDT((CTSdtBlock)xmlcursor.getObject(), document);
//System.out.println("block: " + sdt);
allsdts.add(sdt);
}
}
}
}
return allsdts;
}
public static void main(String[] args) throws Exception {
XWPFDocument document = new XWPFDocument(new FileInputStream("WordDataCollectingForm.docx"));
/*
List<IBodyElement> bodyelements = document.getBodyElements();
List<AbstractXWPFSDT> sdts = extractSDTsFromBodyElements(bodyelements);
*/
List<XWPFSDT> allsdts = extractSDTsFromBody(document);
for (XWPFSDT sdt : allsdts) {
//System.out.println(sdt);
String title = sdt.getTitle();
String content = sdt.getContent().getText();
if (!(title == null) && !(title.isEmpty())) {
System.out.println(title + ": " + content);
} else {
System.out.println("====sdt without title====");
}
}
document.close();
}
}

Related

How to get the Table inside a MS Word bookmark and add multiple rows based on its Mergefield cells using docx4j?

Till now am able to parse a docx file using docx4j and find the bookmarks and all the tables in a docx file using below code:
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new java.io.File(docxFile));
List<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);
for (Object p : paragraphs) {
RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
new TraversalUtil(p, rt);
for (CTBookmark content : rt.getStarts()) {
if (content.getName().equals("if_supdef")) {
List<Object> tbl = getAllElementFromObject(content, Tbl.class);
System.out.println("tbl==" + tbl.size());
}
}
}
TableFinder finder = new TableFinder();
new TraversalUtil(documentPart.getContent(), finder);
System.out.println("Found " + finder.tblList.size() + " tables");
I've got these lines of code from some blogs and answers from other questions.
Now I would like to find the table only inside a bookmark (here my bookmark name is if_supdef) rather than searching in entire document. Once I find the table, I would add rows based on number of data I receive from SQL table and MERGEFIELDS available.
Bookmark and its table look like something in below picture:
Once processed through docx4j it should look like:
In document.xml I see parent tag of w:tbl is body but not bookmark.
Is it possible to read the table inside bookmark? If so, how?
If not, what is the other alternative to uniquely identify a table and add contents to it?
Try something along the lines of the below.
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.docx4j.TraversalUtil;
import org.docx4j.TraversalUtil.CallbackImpl;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.CTMarkupRange;
import org.docx4j.wml.Tbl;
import jakarta.xml.bind.JAXBContext;
public class TableInBookmarkFinder {
public static JAXBContext context = org.docx4j.jaxb.Context.jc;
public static void main(String[] args) throws Exception {
String inputfilepath = System.getProperty("user.dir")
+ "/tbl_bookmarks.docx";
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage
.load(new java.io.File(inputfilepath));
MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
// find
TableInBookmarkFinderCallback finder = new TableInBookmarkFinderCallback();
new TraversalUtil(documentPart.getContent(), finder);
List<TableInfo> tableInfos = finder.getTableInfos();
// result?
for (TableInfo ti : tableInfos) {
System.out.println("table contained in bookmarks:");
for (String s: ti.getBookmarkNames()) {
System.out.println("bookmark name: " + s);
}
}
}
public static class TableInfo {
TableInfo(Tbl tbl, List<String> bookmarkNames) {
this.tbl = tbl;
this.bookmarkNames = bookmarkNames;
}
private Tbl tbl;
public Tbl getTbl() {
return tbl;
}
private List<String> bookmarkNames;
public List<String> getBookmarkNames() {
return bookmarkNames;
}
}
public static class TableInBookmarkFinderCallback extends CallbackImpl {
public TableInBookmarkFinderCallback() {
}
/**
* Keep this set to true unless you don't
* want to traverse a table (eg a nested table).
* NB: If traversing from body level, you'll need to set it to true!
*/
private boolean traverseTables=true;
/**
* Track bookmarks encountered
*/
private Map<BigInteger, String> bookmarkInfos = new HashMap<BigInteger, String>();
/**
* What bookmarks are we currently in?
*/
private Set<BigInteger> currentBookmarks = new HashSet<BigInteger>();
/**
* What tables did we encounter?
*/
private List<TableInfo> tableInfos = new ArrayList<TableInfo>();
public List<TableInfo> getTableInfos() {
return tableInfos;
}
#Override
public List<Object> apply(Object o) {
System.out.println(o.getClass().getName());
if (o instanceof CTBookmark) {
CTBookmark bmStart = (CTBookmark)o;
bookmarkInfos.put(bmStart.getId(), bmStart.getName());
if (currentBookmarks.add(bmStart.getId()) ) {
// ok
System.out.println("added " + bmStart.getId());
} else {
System.out.println("ERROR: duplicate bookmarks with id " + bmStart.getId());
}
} else /* need this else because CTBookmark extends CTMarkupRange */
if (o instanceof CTMarkupRange) {
CTMarkupRange bmEnd = (CTMarkupRange)o;
if (currentBookmarks.remove(bmEnd.getId()) ) {
// ok
System.out.println("removed " + bmEnd.getId());
} else {
System.out.println("ERROR: no start element for bookmark with id " + bmEnd.getId());
}
}
if (o instanceof Tbl ) {
System.out.println("tbl");
List<String> bookmarkNames = new ArrayList<String>();
for (BigInteger bmId : currentBookmarks) {
bookmarkNames.add(bookmarkInfos.get(bmId));
}
tableInfos.add( new TableInfo( (Tbl)o, bookmarkNames));
}
return null;
}
#Override
public boolean shouldTraverse(Object o) {
if (traverseTables) {
return true;
} else {
// Yes, unless its a nested Tbl
return !(o instanceof Tbl);
}
}
}
}

How do I save Xml Changes Back to the Original Document

I need to update the Styles (styles.xml) part of an MS Word document due to a problem with a vendor's product.
So far I've been able to extract and update the xml I need. The only problem, is that I don't know how to save my changes back to the document.
The code below is working just fine. I usually output the xml to the console to make sure it's going in just fine. At the end, I know I need to perform some save operation, but the XDocument.Save( /stream/) hasn't worked.
Here's where I am so far
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
Console.WriteLine(stylesPart.Styles.OuterXml);
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
}
}
}
I found the answer in the SAVE THE PARTS section of this page Replace the styles parts in a word processing document (Open XML SDK)
See the end of this code for the solution. You'll also see what I've tried.
static void FixNormal()
{
using (WordprocessingDocument doc = WordprocessingDocument.Open(_path, true))
{
// Get the Styles part for this document.
StyleDefinitionsPart stylesPart = doc.MainDocumentPart.StyleDefinitionsPart;
// If the Styles part does not exist, add it and then add the style.
if (stylesPart == null)
{
Console.WriteLine("No Style Part");
}
else
{
XDocument stylesDoc;
using (var reader = XmlNodeReader.Create(stylesPart.GetStream(FileMode.Open, FileAccess.Read)))
{
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
// Create the XDocument.
stylesDoc = XDocument.Load(reader);
var xStyle = stylesDoc.Descendants(w + "styles").Descendants(w + "style").Where(x => x.Attribute(w + "styleId").Value.Equals("Normal"));
XElement style = xStyle.Single();
var q = style.Descendants(w + "qFormat").FirstOrDefault();
if (q is null)
{
XElement qFormat = new XElement(w + "qFormat");
style.Add(qFormat);
}
var r = style.Descendants(w + "rsid").FirstOrDefault();
if (r is null)
{
XElement rsid = new XElement(w + "rsid");
XAttribute val = new XAttribute(w + "val", "003C4F1E");
rsid.Add(val);
style.Add(rsid);
}
}
//doc.Save(); --- Did not work
//stylesDoc.Save(#"C:\WinTest\HooRah.xml"); -- I only use this to verify that I've updated everything correctly
//using (XmlWriter xw = XmlWriter.Create(stylesPart.GetStream(FileMode.Create, FileAccess.Write)))
//{
// stylesDoc.Save(xw); -- DID NOT WORK EITHER
// doc.Save();
//}
// THIS WORKED
stylesDoc.Save(new StreamWriter(stylesPart.GetStream(FileMode.Create, FileAccess.Write)));
}
}
}

How do I find which image field in PDF has image inserted and which one has no images attached using PDFbox 1.8.11?

I have a PDF that has image fields inside it. I am not using a PDPushButton with javascript to attach pictures because if I do that the button's top layer gets replaced with the picture that I am attaching which is not what I want. So I am explicitly using a ImageField that is available in Adobe LiveCycle Designer. I am able to extract the files attached on it using PDFBox but I am not able to find any way of seeing which image fields have files attached to them and which ones do not. For example if I have the following code here:
ImageField[1], ImageField[2], ImageField[3]
I want to see something like
ImageField[1]: null,
ImageField[2]: true,
ImageField[3]: trueenter code here
etc assuming ImageField[2] and ImageField[3] has images attached to them.
Below is the code that I was working on:
I have a constant:
Then I am looping through the whole set of image field names and see which field is a instance of PDXObjectImage and then if it is a PDXObjectImage then I check if that object.getRGBImage().getHeight() > 0 assuming that only files uploaded have a height > 1 which means a file has been attached.
private static String[] IMAGE_FIELD_ROW = {"ImageField1[0]","ImageField2[0]",....} => 100 rows of string values such as "ImageField3[0]", "ImageField4[0]", ...etc.
for(int i = 0; i<IMAGE_FIELD_ROW.length; i++)
{
if(field.getPartialName().equals(IMAGE_FIELD_ROW[i]))
{
Map<String, PDAppearanceStream> stateAppearances = field.getWidget().getAppearance().getNormalAppearance();
for (Map.Entry<String, PDAppearanceStream> entry: stateAppearances.entrySet())
{
PDAppearanceStream appearance = entry.getValue();
PDResources resources = appearance.getResources();
if (resources == null)
return;
Map<String, PDXObject> xObjects = resources.getXObjects();
if (xObjects == null)
return;
for (Map.Entry<String, PDXObject> entryNew : xObjects.entrySet())
{
PDXObject xObject = entryNew.getValue();
System.out.println("printing out the xobject name: "+ entryNew.getKey());
if (xObject instanceof PDXObjectForm)
{
PDXObjectForm form = (PDXObjectForm)xObject;
PDResources resources2 = form.getResources();
if (resources2 == null)
return;
Map<String, PDXObject> xObjects2 = resources2.getXObjects();
if (xObjects2 == null)
{
return;
}
for (Map.Entry<String, PDXObject> entry2 : xObjects2.entrySet())
{
PDXObject xObject2 = entry2.getValue();
if (xObject2 instanceof PDXObjectForm)
{
continue;
}
else if (xObject2 instanceof PDXObjectImage)
{
PDXObjectImage ig = (PDXObjectImage)xObject2;
if(ig.getRGBImage().getHeight() > 0)
{
images.put(field.getPartialName(), "true");
}
else
{
images.put(field.getPartialName(), null);
}
//imageIds.add(imageId);
}
else
{
continue;
}
}
}
}
}
}
}
Images is a map variable: Mapimages.
Also my code file is large and so I didn't want to overwhelm anybody by pasting the whole file. Below is the dropbox link for the sample PDF file that I am using:
https://www.dropbox.com/s/g2wqm8ipsp8t8l5/GSA%20500%20PDF_v4.pdf?dl=0
Your PDF is a hybrid AcroForm/XFA document; where the XFA part uses fields with an imageEdit user interface, the AcroForm part uses pushbutton fields.
Thus, it allows you two ways to check whether an image field is set: Either you look at the AcroForm buttons and inspect their appearances for images, or you retrieve the XFA XML and inspect that.
Checking the XFA XML
Initially I did overlook the PDFBox version in the question title and implemented this for PDFBox 2.0.x. As it turns out, though, the identical code can be used for PDFBox 1.8.11, merely some additional exceptions may be thrown and, therefore, must be considered.
The latter option, inspecting the XFA XML, actually is a bit easier for the document at hand. Simply search the XML for an element with the name in question and check its contents. As an additional sanity check one can verify the content type attribute of the element:
boolean isFieldFilledXfa(Document xfaDom, String fieldName) {
NodeList fieldElements = xfaDom.getElementsByTagName(fieldName);
for (int i = 0; i < fieldElements.getLength(); i++) {
Node node = fieldElements.item(i);
if (node instanceof Element) {
Element element = (Element) node;
if (element.getAttribute("xfa:contentType").startsWith("image/")) {
return element.getTextContent().length() > 0;
}
}
}
return false;
}
(CheckImageFieldFilled helper method)
With it you can check your document:
PDDocument document = PDDocument.load(SOURCE);
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
Document xfaDom = acroForm.getXFA().getDocument();
System.out.println("Filled image fields from ImageField1..ImageField105:");
for (int i=1; i < 106; i++) {
if (isFieldFilledXfa(xfaDom, "ImageField" + i)) {
System.out.printf("* ImageField%d\n", i);
}
}
(CheckImageFieldFilled test method testCheckXfaGsa500Pdf_v4)
The output:
Filled image fields from ImageField1..ImageField105:
* ImageField1
* ImageField3
* ImageField6
Checking the AcroForm Appearances
The implementation here only works as is for PDFBox 2.0.x. The structure of the content stream parser classes has been considerably overhauled in 2.0.0, making a back-port of this code to 1.8.x a bit tedious.
To check whether the push button appearance actually shows an image (and not only has an image in its resources), one can use a simple PDFGraphicsStreamEngine subclass like this:
public class WidgetImageChecker extends PDFGraphicsStreamEngine
{
public WidgetImageChecker(PDAnnotationWidget widget) {
super(widget.getPage());
this.widget = widget;
}
public boolean hasImages() throws IOException {
count = 0;
PDAppearanceStream normalAppearance = widget.getNormalAppearanceStream();
processChildStream(normalAppearance, widget.getPage());
return count != 0;
}
#Override
public void drawImage(PDImage pdImage) throws IOException {
count++;
}
#Override
public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException { }
#Override
public void clip(int windingRule) throws IOException { }
#Override
public void moveTo(float x, float y) throws IOException { }
#Override
public void lineTo(float x, float y) throws IOException { }
#Override
public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException { }
#Override
public Point2D getCurrentPoint() throws IOException { return null; }
#Override
public void closePath() throws IOException { }
#Override
public void endPath() throws IOException { }
#Override
public void strokePath() throws IOException { }
#Override
public void fillPath(int windingRule) throws IOException { }
#Override
public void fillAndStrokePath(int windingRule) throws IOException { }
#Override
public void shadingFill(COSName shadingName) throws IOException { }
final PDAnnotationWidget widget;
int count = 0;
}
(CheckImageFieldFilled helper class)
With it you can create a check method like this:
boolean isFieldFilledAcroForm(PDAcroForm acroForm, String fieldName) throws IOException {
for (PDField field : acroForm.getFieldTree()) {
if (field instanceof PDPushButton && fieldName.equals(field.getPartialName())) {
for (final PDAnnotationWidget widget : field.getWidgets()) {
WidgetImageChecker checker = new WidgetImageChecker(widget);
if (checker.hasImages())
return true;
}
}
}
return false;
}
(CheckImageFieldFilled helper method)
and use it like this:
PDDocument document = PDDocument.load(SOURCE);
PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
System.out.println("Filled image fields (AcroForm) from ImageField1..ImageField105:");
for (int i=1; i < 106; i++) {
if (isFieldFilledAcroForm(acroForm, "ImageField" + i + "[0]")) {
System.out.printf("* ImageField%d\n", i);
}
}
(CheckImageFieldFilled test testCheckAcroFormGsa500Pdf_v4)
The output, just like above:
Filled image fields (AcroForm) from ImageField1..ImageField105:
* ImageField1
* ImageField3
* ImageField6

Streaming in datamapper in mule esb

I need to take data(input.xml) from one file which is size in 100MB-200MB and need to write into four different files based on some logic.
input xml :
<?xml version="1.0"?>
<Orders>
<Order><OrderId>1</OrderId><Total>10</Total><Name>jon1</Name></Order>
<Order><OrderId>2</OrderId><Total>20</Total><Name>jon2</Name></Order>
<Order><OrderId>3</OrderId><Total>30</Total><Name>jon3</Name></Order>
<Order><OrderId>4</OrderId><Total>40</Total><Name>jon4</Name></Order>
<Orders>
logic is if Total is 1-10 then write to file1 and if Total is 11-20 then write to file2.....,
expected output:
1 10 jon1 -->write into file1
2 20 jon2 -->write into file2
3 30 jon3 -->write into file3
4 40 jon4 -->write into file4
Here i have enabled streaming in datamapper which is under configuration but i'm not getting proper output. The problem is i'm getting only some recodes into only one file which should come into that file after satisfying the condition.
But if i disable streaming button in datamapper it is working fine. As there are lakes of records i must use streaming option.
Is there any otherway to configure datamapper to enable streaming option..?
Please suggest me on this., Thanks.,
It is difficult to see a problem without a little more detail on what you are doing.
Nevertheless, I think this probably will help you to try another approach.
The data mapper will load the full XML document into memory although you activate streaming, it has to do it in order to support XPATH (it loads the full xml input into a DOM).
So if you can not afford to load 200Mb document into memory you will need to try a workaround.
What I have done before is creating a java component that transforms the input stream to an iterator with the help of a stax parser. With a very simple implementation you can code an iterator that pulls from the stream to create the next element (a pojo, a map, a string...). In the mule flow, after the "java component", you should be able to use a "for-each" with a "choice" within and apply your logic.
A quick example for your data:
package tests;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class OrdersStreamIterator implements Iterator<Map<String,String>> {
final static Log LOGGER = LogFactory.getLog(OrdersStreamIterator.class);
final InputStream is;
final XMLStreamReader xmlReader;
boolean end = false;
HashMap<String,String> next;
public OrdersStreamIterator(InputStream is)
throws XMLStreamException, FactoryConfigurationError {
this.is = is;
xmlReader = XMLInputFactory.newInstance().createXMLStreamReader(is);
}
protected HashMap<String,String> _next() throws XMLStreamException {
int event;
HashMap<String,String> order = null;
String orderChild = null;
String orderChildValue = null;
while (xmlReader.hasNext()) {
event = xmlReader.getEventType();
if (event == XMLStreamConstants.START_ELEMENT) {
if (order==null) {
if (checkOrder()) {
order = new HashMap<String,String>();
}
}
else {
orderChild = xmlReader.getLocalName();
}
}
else if (event == XMLStreamConstants.END_ELEMENT) {
if (checkOrders()) {
end = true;
return null;
}
else if (checkOrder()) {
xmlReader.next();
return order;
}
else if (order!=null) {
order.put(orderChild, orderChildValue);
orderChild = null;
orderChildValue = null;
}
}
else if (order!=null && orderChild!=null){
switch (event) {
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.CDATA:
int start = xmlReader.getTextStart();
int length = xmlReader.getTextLength();
if (orderChildValue==null) {
orderChildValue = new String(xmlReader.getTextCharacters(), start, length);
}
else {
orderChildValue += new String(xmlReader.getTextCharacters(), start, length);
}
break;
}
}
xmlReader.next();
}
end = true;
return null;
}
protected boolean checkOrder() {
return "Order".equals(xmlReader.getLocalName());
}
protected boolean checkOrders() {
return "Orders".equals(xmlReader.getLocalName());
}
#Override
public boolean hasNext() {
if (end) {
return false;
}
else if (next==null) {
try {
next = _next();
} catch (XMLStreamException e) {
LOGGER.error(e.getMessage(), e);
end = true;
}
return !end;
}
else {
return true;
}
}
#Override
public Map<String,String> next() {
if (hasNext()) {
final HashMap<String,String> n = next;
next = null;
return n;
}
else {
return null;
}
}
#Override
public void remove() {
throw new RuntimeException("ReadOnly!");
}
// Test
public static String dump(Map<String,String> o) {
String s = "{";
for (Entry<String,String> e : o.entrySet()) {
if (s.length()>1) {
s+=", ";
}
s+= "\"" + e.getKey() + "\" : \"" + e.getValue() + "\"";
}
return s + "}";
}
public static void main(String[] argv) throws XMLStreamException, FactoryConfigurationError {
final InputStream is = OrdersStreamIterator.class.getClassLoader().getResourceAsStream("orders.xml");
final OrdersStreamIterator i = new OrdersStreamIterator(is);
while (i.hasNext()) {
System.out.println(dump(i.next()));
}
}
}
An example flow:
<flow name="testsFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<scripting:component doc:name="Groovy">
<scripting:script engine="Groovy"><![CDATA[return tests.OrdersStreamIterator.class.getClassLoader().getResourceAsStream("orders.xml");]]></scripting:script>
</scripting:component>
<set-payload value="#[new tests.OrdersStreamIterator(payload)]" doc:name="Iterator"/>
<foreach doc:name="For Each">
<logger message="#[tests.OrdersStreamIterator.dump(payload)]" level="INFO" doc:name="Logger"/>
</foreach>
</flow>

GWT-RPC method returns empty list on success

I am creating a webpage having CellTable.I need to feed this table with data from hbase table.
I have written a method to retrieve data from hbase table and tested it.
But when I call that method as GWT asynchronous RPC method then rpc call succeeds but it returns nothing.In my case it returns empty list.The alert box show list's size as 0.
Following is the related code.
Please help.
greetingService.getDeviceIDData(new AsyncCallback<List<DeviceDriverBean>>(){
public void onFailure(Throwable caught) {
// Show the RPC error message to the user
System.out.println("RPC Call failed");
Window.alert("Data : RPC call failed");
}
public void onSuccess(List<DeviceDriverBean> result) {
//on success do something
Window.alert("Data : RPC call successful");
//deviceDataList.addAll(result);
Window.alert("Result size: " +result.size());
// Add a text column to show the driver name.
TextColumn<DeviceDriverBean> nameColumn = new TextColumn<DeviceDriverBean>() {
#Override
public String getValue(DeviceDriverBean object) {
Window.alert(object.getName());
return object.getName();
}
};
table.addColumn(nameColumn, "Name");
// Add a text column to show the device id
TextColumn<DeviceDriverBean> deviceidColumn = new TextColumn<DeviceDriverBean>() {
#Override
public String getValue(DeviceDriverBean object) {
return object.getDeviceId();
}
};
table.addColumn(deviceidColumn, "Device ID");
table.setRowCount(result.size(), true);
// more code here to add columns in celltable
// Push the data into the widget.
table.setRowData(0, result);
SimplePager pager = new SimplePager();
pager.setDisplay(table);
VerticalPanel vp = new VerticalPanel();
vp.add(table);
vp.add(pager);
// Add it to the root panel.
RootPanel.get("datagridContainer").add(vp);
}
});
Code to retrieve data from hbase (server side code)
public List<DeviceDriverBean> getDeviceIDData()
throws IllegalArgumentException {
List<DeviceDriverBean> deviceidList = new ArrayList<DeviceDriverBean>();
// Escape data from the client to avoid cross-site script
// vulnerabilities.
/*
* input = escapeHtml(input); userAgent = escapeHtml(userAgent);
*
* return "Hello, " + input + "!<br><br>I am running " + serverInfo +
* ".<br><br>It looks like you are using:<br>" + userAgent;
*/
try {
Configuration config = HbaseConnectionSingleton.getInstance()
.HbaseConnect();
HTable testTable = new HTable(config, "driver_details");
byte[] family = Bytes.toBytes("details");
Scan scan = new Scan();
int cnt = 0;
ResultScanner rs = testTable.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
DeviceDriverBean deviceDriverBean = new DeviceDriverBean();
byte[] rowid = r.getRow(); // Category, Date, Sentiment
NavigableMap<byte[], byte[]> map = r.getFamilyMap(family);
Iterator<Entry<byte[], byte[]>> itrt = map.entrySet()
.iterator();
deviceDriverBean.setDeviceId(Bytes.toString(rowid));
while (itrt.hasNext()) {
Entry<byte[], byte[]> entry = itrt.next();
//cnt++;
//System.out.println("Count : " + cnt);
byte[] qual = entry.getKey();
byte[] val = entry.getValue();
if (Bytes.toString(qual).equalsIgnoreCase("account_number")) {
deviceDriverBean.setAccountNo(Bytes.toString(val));
} else if (Bytes.toString(qual).equalsIgnoreCase("make")) {
deviceDriverBean.setMake(Bytes.toString(val));
} else if (Bytes.toString(qual).equalsIgnoreCase("model")) {
deviceDriverBean.setModel(Bytes.toString(val));
} else if (Bytes.toString(qual).equalsIgnoreCase("driver_name")) {
deviceDriverBean.setName(Bytes.toString(val));
} else if (Bytes.toString(qual).equalsIgnoreCase("premium")) {
deviceDriverBean.setPremium(Bytes.toString(val));
} else if (Bytes.toString(qual).equalsIgnoreCase("year")) {
deviceDriverBean.setYear(Bytes.toString(val));
} else {
System.out.println("No match found");
}
/*
* System.out.println(Bytes.toString(rowid) + " " +
* Bytes.toString(qual) + " " + Bytes.toString(val));
*/
}
deviceidList.add(deviceDriverBean);
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (Exception e) {
// System.out.println("Message: "+e.getMessage());
e.printStackTrace();
}
return deviceidList;
}
Could this be lazy fetching on the server side by hbase. This means if you return the list hbase won't get a trigger to actually read the list and you will simple get an empty list. I don't know a correct solution, in the past I've seen a similar problem on GAE. This could by solved by simply asking the size of the list just before returning it to the client.
I don't have the exact answer, but I have an advise. In similar situation I put my own trace to check every step in my program.
On the server side before return put : System.out.println("size of table="+deviceidList.size());
You can put this trace in the loop for deviceidList;