unexpected instructions and parameters for invokevirtual in the inlined method body - bytecode

I followed the sample code in the "3.2.6 Inline Method“ in the http://asm.ow2.org/current/asm-transformations.pdf, to inline a MethodNode to a call site.
My problem is that there are some unexpected instructions shown in the generated bytecode after inlining (these bytecodes are inconsistent to my code), and the problem only exists when an ifeq is after inlineed method body and the variable on the stack is loaded by xLoad.
I still have not found the root cause for the issue. Now i am start to remove all unncessary codes, aiming to reproduce it with least code. Anyone has good suggestions are welcome.
Here is one of my existing founding: the problem is not related to the Frame, because the problem is still there when Configuration for ClassRewiter is COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS and Configuration for ClassReader ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES
To simplify the problem, the callee's body is:
public invokeExact(Ljava/lang/String;)Z
           ICONST_0
           IRETURN
 And the Caller is:
public String invokeExact(String a, String b){
         boolean flag = _guard.invokeExact(a);
         if(flag)
         {
            return a;
         }
         return b;
      }
. The corresponding bytecode manipulation trace of the caller on the MethodWriter is:
public java.lang.String invokeExact(java.lang.String, java.lang.String)
       ....
         4: aload_1      
         5: astore_3     
         6: astore        4
         8: iconst_0     
         visitJumpInsn  goto    L1029004533
          //visitmax()  empty implementation.
          //visitEnd() Empty implementation.
          visitlabel    L1029004533   // This label is newly created once inlining starts, but is visited until the end of inlining as the target of all xReturn instructions in the Callee's method body.
       visitVarInsn  istore 5
       visitVarInsn  iload  5
       visitJumpInsn  ifeq  L980604133
       visitVarInsn   aload 1
       visitInsn        areturn
       visitLabel      L980604133
       visitVarInsn   aload 2
       visitInsn        areturn
Finally, the generated class file is:
 
public java.lang.String invokeExact(java.lang.String, java.lang.String);
    stack=2, locals=6, args_size=3
         0: aload_0      
         1: getfield      #17                 // Field _guard:Ltest/code/jit/asm/simple/MHGuard;
         4: aload_1      
         5: astore_3     
         6: astore        4
         8: iconst_0     
         **9: goto          9
        12: fconst_0     
        13: iconst_2**     
        14: iload         5
        16: ifeq          21
        19: aload_1      
        20: areturn      
        21: aload_2      
        22: areturn      
      StackMapTable: number_of_entries = 2
           frame_type = 255 /* full_frame */
          offset_delta = 12
          locals = [ class test/code/jit/asm/simple/GWTSample, class java/lang/String, class java/lang/String, class java/lang/String, class test/code/jit/asm/simple/MHGuard ]
          stack = [ int ]
           frame_type = 252 /* append */
             offset_delta = 8
        locals = [ int ]
where #9, #12, and #13 are wrong.
Parts of my code are (I will continue to simple my code on the weekend):
public class MethodCallInliner extends LocalVariablesSorter {
protected MethodContext _context;
private IPlugin _plugin;
public MethodCallInliner(int access, String desc, MethodContext context){
// context.getRawMV() return a Class MethodWriter.
super(Opcodes.ASM5, access, desc, context.getRawMV());
_context = context;
//_fieldVisitor = new FieldManipulationVisitor(mv, context);
_plugin = NameMappingService.get().getPlugin();
//removed some unncessary codes..
}
#Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
if(opcode != Opcodes.INVOKEVIRTUAL){
mv.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
MethodNode mn = _plugin.map(owner, name, desc, _context, this);
if(mn == null){
mv.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
//ASMUtil.debug(mn); //to double confirm the mn content is correct.
performInline(ASMUtil.isStaticMethod(mn)?Opcodes.INVOKESTATIC:Opcodes.INVOKEVIRTUAL, owner, desc, mn);
_plugin.postProcess(mn, this, _context);
}
protected void performInline(int opcode, String owner, String desc, MethodNode mn){
Remapper remapper = Mapper.getMapper(_context, _context.getReceiverFieldName());
mn.instructions.resetLabels();
Label end = new Label();
System.out.println("++"+end.toString());
_context.beginInline();
mn.accept(new InliningAdapter(this,
opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc,
remapper, end, _context));
_context.endInline();
super.visitLabel(end);
}
public void visitJumpInsn(int opcode, Label label) {
super.visitJumpInsn(opcode, label);
}
#Override
public void visitVarInsn(final int opcode, final int var){
super.visitVarInsn(opcode, var);;
}
...
}
[New Findings]
I think I am much closer to the problem now.
The inlining visitor MethodCallInliner should be correct as another independent testing of this visitor with the same classes succeeds.
The issue is at the way how to build the MethodVisitor chain. a) I want only one pass visiting on the Method instructions. 2) The MethodCallInliner is arranged at the end of the chain. Before it, some more visitors are inserted to inference type information, which would possible used during method inlining in the MethodCallInliner.
My chain builder is:
#Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
.....
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
return new TransformationChain(Opcodes.ASM5, access, name, desc, signature, mv, _context);
//return new MethodCallInliner(access, desc, context); //This is OK.
}
public class TransformationChain extends BaseMethodTransform {
public TransformationChain(int api, int access, String name, String desc, String signature, MethodVisitor mv, ClassContext classContext) {
super(api, mv, classContext.getClassName(), name, desc);
....
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
_visitors.add(new AnalyzerAdapter(Opcodes.ASM5, owner, access, name,desc, cw.visitMethod(access, name, desc, owner, null)){
#Override
public void visitJumpInsn(final int opcode, final Label label){
super.visitJumpInsn(opcode, label);
}
});
MethodNode node = new MethodNode(access, name, desc, signature, null);
_visitors.add(node);
//cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
//MethodNode node = context.getClassContext().getMethodNode(name, desc);
//_visitors.add(new TypeInferencer(Opcodes.ASM5, cw.visitMethod(access, name, desc, null, null), node, context));
_visitors.add(name.equals(Constants.CONSTRUCTOR)?new ConstructorMerge(access, desc, context):
new MethodCallInliner(access, desc, context));
}
}
abstract class BaseMethodTransform extends MethodVisitor {
protected final List<MethodVisitor> _visitors = new LinkedList<MethodVisitor>();
public BaseMethodTransform(int api, MethodVisitor mv, String className, String methodName, String methodDesc) {
super(api, mv);
}
#Override
public void visitMethodInsn(int opcode, String owner, String name,
String desc, boolean itf) {
for (MethodVisitor mv : _visitors) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
#Override
public void visitIntInsn(int opcode, int operand) {
for (MethodVisitor mv : _visitors) {
mv.visitIntInsn(opcode, operand);
}
}
#Override
public void visitMaxs(int maxStack, int maxLocals) {
for (MethodVisitor mv : _visitors) {
if (mv!= _visitors.get(_visitors.size()-1) || mv instanceof TraceMethodVisitor) {
continue;
}
mv.visitMaxs(maxStack, maxLocals);
}
}
#Override
public void visitJumpInsn(final int opcode, final Label label) {
for (MethodVisitor mv : _visitors) {
mv.visitJumpInsn(opcode, label);
}
}
......
}
My Finding here is that the generated class is correct if I comment out _visitors.add(new AnalyzerAdapter..); in the TransformationChain, the MethodVisitor of which is newly created here. It seems that some elements of a method have status, which might be modified by MethodWriters (even they are all independent) and the previous modification has impacts on the later visitors.
I also noticed it is the Label:
/**
* Informations about forward references. Each forward reference is
* described by two consecutive integers in this array: the first one is the
* position of the first byte of the bytecode instruction that contains the
* forward reference, while the second is the position of the first byte of
* the forward reference itself. In fact the sign of the first integer
* indicates if this reference uses 2 or 4 bytes, and its absolute value
* gives the position of the bytecode instruction. This array is also used
* as a bitset to store the subroutines to which a basic block belongs. This
* information is needed in {#linked MethodWriter#visitMaxs}, after all
* forward references have been resolved. Hence the same array can be used
* for both purposes without problems.
*/
private int[] srcAndRefPositions;
When it is first visited by AnalyzerAdapter::visitJmpAdadpter, two ints, e.g., 10 and 11, are inserted at the begin of the array. Then in the next iteration ``MethodCallInliner::visitJmpInsn`, another two new ints are added at position 2 and 3. Now the array content is:
[10, 11, 16, 17, 0, 0]
in which the pair (10,11) is for AnalyzerAdapter and the pair (16,17) is for Method MethodCallInliner.
But what puzzles me here is: the ASM should be able to distinct different pairs for the right MethodVisitor when generating the bytcode class (or block, stack frame calculation whatever)?
The code can be accessed by https://github.com/xushijie/InlineMethod/tree/typeinference

The problem is caused when the label (the classreader reads from a class file) is visited by a MethodVisitor pipeline. The label has a field int [] srcAndRefPositions. Two of its consecutive positions (cfr. the end of my original post) are updated once the label is accessed by a MethodVisitor. In my case, the label in the ifeq label holds 2 MethodVisitors. It seems the incorrect position in the srcAndRefPositions is used when generating the class file (using the last MethodVisitor).
I did not investigate the root cause. Instead, my solution was to clone the label and then use the new label when it is visited by a MethodVisitor.

Related

How to read numeric value from excel file using spring batch excel

I am reading values from .xlsx using spring batch excel and POI. I see numeric values are printing with different format than the original value in .xlsx
Please suggest me , How to print the values as its in .xlsx file. Below are the details.
In my Excel values are as follows
The values are printing as below
My code is as below
public ItemReader<DataObject> fileItemReader(InputStream inputStream){
PoiItemReader<DataObject> reader = new PoiItemReader<DataObject>();
reader.setLinesToSkip(1);
reader.setResource(new InputStreamResource(DataObject));
reader.setRowMapper(excelRowMapper());
reader.open(new ExecutionContext());
return reader;
}
private RowMapper<DataObject> excelRowMapper() {
return new MyRowMapper();
}
public class MyRowMapper implements RowMapper<DataObject> {
#Override
public DataRecord mapRow(RowSet rowSet) throws Exception {
DataObject dataObj = new DataObject();
dataObj.setFieldOne(rowSet.getColumnValue(0));
dataObj.setFieldTwo(rowSet.getColumnValue(1));
dataObj.setFieldThree(rowSet.getColumnValue(2));
dataObj.setFieldFour(rowSet.getColumnValue(3));
return dataObj;
}
}
I had this same problem, and its root is the class org.springframework.batch.item.excel.poi.PoiSheet inside PoiItemReader.
The problem happens in the method public String[] getRow(final int rowNumber) where it gets a org.apache.poi.ss.usermodel.Row object and convert it to an array of Strings after detecting the type of each column in the row. In this method, we have the code:
switch (cellType) {
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
cells.add(String.valueOf(date.getTime()));
} else {
cells.add(String.valueOf(cell.getNumericCellValue()));
}
break;
case BOOLEAN:
cells.add(String.valueOf(cell.getBooleanCellValue()));
break;
case STRING:
case BLANK:
cells.add(cell.getStringCellValue());
break;
case ERROR:
cells.add(FormulaError.forInt(cell.getErrorCellValue()).getString());
break;
default:
throw new IllegalArgumentException("Cannot handle cells of type '" + cell.getCellTypeEnum() + "'");
}
In which the treatment for a cell identified as NUMERIC is cells.add(String.valueOf(cell.getNumericCellValue())). In this line, the cell value is converted to double (cell.getNumericCellValue()) and this double is converted to String (String.valueOf()). The problem happens in the String.valueOf() method, that will generate scientific notation if the number is too big (>=10000000) or too small(<0.001) and will put the ".0" on integer values.
As an alternative to the line cells.add(String.valueOf(cell.getNumericCellValue())), you could use
DataFormatter formatter = new DataFormatter();
cells.add(formatter.formatCellValue(cell));
that will return to you the exact values of the cells as a String. However, this also mean that your decimal numbers will be locale dependent (you'll receive the string "2.5" from a document saved on an Excel configured for UK or India and the string "2,5" from France or Brazil).
To avoid this dependency, we can use the solution presented on https://stackoverflow.com/a/25307973/9184574:
DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340);
cells.add(df.format(cell.getNumericCellValue()));
That will convert the cell to double and than format it to the English pattern without scientific notation or adding ".0" to integers.
My implementation of the CustomPoiSheet (small adaptation on original PoiSheet) was:
class CustomPoiSheet implements Sheet {
protected final org.apache.poi.ss.usermodel.Sheet delegate;
private final int numberOfRows;
private final String name;
private FormulaEvaluator evaluator;
/**
* Constructor which takes the delegate sheet.
*
* #param delegate the apache POI sheet
*/
CustomPoiSheet(final org.apache.poi.ss.usermodel.Sheet delegate) {
super();
this.delegate = delegate;
this.numberOfRows = this.delegate.getLastRowNum() + 1;
this.name=this.delegate.getSheetName();
}
/**
* {#inheritDoc}
*/
#Override
public int getNumberOfRows() {
return this.numberOfRows;
}
/**
* {#inheritDoc}
*/
#Override
public String getName() {
return this.name;
}
/**
* {#inheritDoc}
*/
#Override
public String[] getRow(final int rowNumber) {
final Row row = this.delegate.getRow(rowNumber);
if (row == null) {
return null;
}
final List<String> cells = new LinkedList<>();
final int numberOfColumns = row.getLastCellNum();
for (int i = 0; i < numberOfColumns; i++) {
Cell cell = row.getCell(i);
CellType cellType = cell.getCellType();
if (cellType == CellType.FORMULA) {
FormulaEvaluator evaluator = getFormulaEvaluator();
if (evaluator == null) {
cells.add(cell.getCellFormula());
} else {
cellType = evaluator.evaluateFormulaCell(cell);
}
}
switch (cellType) {
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date date = cell.getDateCellValue();
cells.add(String.valueOf(date.getTime()));
} else {
// Returns numeric value the closer possible to it's value and shown string, only formatting to english format
// It will result in an integer string (without decimal places) if the value is a integer, and will result
// on the double string without trailing zeros. It also suppress scientific notation
// Regards to https://stackoverflow.com/a/25307973/9184574
DecimalFormat df = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
df.setMaximumFractionDigits(340);
cells.add(df.format(cell.getNumericCellValue()));
//DataFormatter formatter = new DataFormatter();
//cells.add(formatter.formatCellValue(cell));
//cells.add(String.valueOf(cell.getNumericCellValue()));
}
break;
case BOOLEAN:
cells.add(String.valueOf(cell.getBooleanCellValue()));
break;
case STRING:
case BLANK:
cells.add(cell.getStringCellValue());
break;
case ERROR:
cells.add(FormulaError.forInt(cell.getErrorCellValue()).getString());
break;
default:
throw new IllegalArgumentException("Cannot handle cells of type '" + cell.getCellTypeEnum() + "'");
}
}
return cells.toArray(new String[0]);
}
private FormulaEvaluator getFormulaEvaluator() {
if (this.evaluator == null) {
this.evaluator = delegate.getWorkbook().getCreationHelper().createFormulaEvaluator();
}
return this.evaluator;
}
}
And my implementation of CustomPoiItemReader (small adaptation on original PoiItemReader) calling CustomPoiSheet:
public class CustomPoiItemReader<T> extends AbstractExcelItemReader<T> {
private Workbook workbook;
#Override
protected Sheet getSheet(final int sheet) {
return new CustomPoiSheet(this.workbook.getSheetAt(sheet));
}
public CustomPoiItemReader(){
super();
}
#Override
protected int getNumberOfSheets() {
return this.workbook.getNumberOfSheets();
}
#Override
protected void doClose() throws Exception {
super.doClose();
if (this.workbook != null) {
this.workbook.close();
}
this.workbook=null;
}
/**
* Open the underlying file using the {#code WorkbookFactory}. We keep track of the used {#code InputStream} so that
* it can be closed cleanly on the end of reading the file. This to be able to release the resources used by
* Apache POI.
*
* #param inputStream the {#code InputStream} pointing to the Excel file.
* #throws Exception is thrown for any errors.
*/
#Override
protected void openExcelFile(final InputStream inputStream) throws Exception {
this.workbook = WorkbookFactory.create(inputStream);
this.workbook.setMissingCellPolicy(Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
}
}
just change your code like this while reading data from excel.
dataObj.setField(Float.valueOf(rowSet.getColumnValue(idx)).intValue();
this is only working for Column A,B,C

Token "{", # expected after this token in enum for processing 2.2.1

I've made a project with processing 2.2.1 and I use an enum. Howerver I called my enum Colour.java, I've got an error:
Syntax error on token "{", # expected after this token.
Here is my code:
public enum Colour
{ // --> on this line
RED({0xFF0000, 0xDD0000, 0x990000, 0x660000, 0x330000}),
GREEN({0x00FF00, 0x00DD00, 0x009900, 0x006600, 0x003300}),
BLUE({0x0000FF, 0x0000DD, 0x000099, 0x000066, 0x000033});
private final int[] shades;
public Colour(int[] shades)
{
this.shades = shades;
}
public int[] getShades()
{
return shades;
}
}
The syntax for creating new int arrays needs to start with new int[]:
RED(new int[] {0xFF0000, 0xDD0000, 0x990000, 0x660000, 0x330000}),
// ^^^^^^^^^
The only time you can omit this is when you're initializing a variable or field at the same time that you declare it:
int[] ints = { 1, 2, 3 };
After that, you will need to reduce the constructor's visibility from public to package-private or private, and then things will work.
You can use varargs
public enum Colour
{ // --> on this line
RED(0xFF0000, 0xDD0000, 0x990000, 0x660000, 0x330000),
GREEN(0x00FF00, 0x00DD00, 0x009900, 0x006600, 0x003300),
BLUE(0x0000FF, 0x0000DD, 0x000099, 0x000066, 0x000033);
private final int[] shades;
Colour(int... shades)
{
this.shades = shades;
}
public int[] getShades()
{
return shades;
}
}

Functional reference in foreach with more than one arguments

I have some situation where I want to pass method reference of a method which have more than one arguments in forEach, I'm able to pass using single arg method which is execute(Map page) but I do not know how could I pass execute(Map page, Integer userInstanceId) in forEach, need help.
static List<Map<String,Object>> pages = new ArrayList<>();
static Map<String,Object> pageIdWithPageInstanceId = new HashMap<>();
public static void testMethodReference() {
TestListMapLamda tt = new TestListMapLamda();
Integer userInstanceId = 22;
pages.stream().filter(page -> null != pageIdWithPageInstanceId.get(page.get("PageID"))).forEach( tt::execute);//this calling the single arg action which is execute(Map<String,Object> page) but not execute(Map<String,Object> page, Integer userInstanceId)
}
public void execute(Map<String,Object> page) {
page.put("UserInstanceID", 1111);
}
public void execute(Map<String,Object> page, Integer userInstanceId) {
page.put("UserInstanceID", userInstanceId);
}
I can call execute(Map page, Integer userInstanceId) by following code, but I'd like to have some more compact stile like the one argument method reference .
public static void testMethodReference2() {
TestListMapLamda tt = new TestListMapLamda();
Integer userInstanceId = 22;
pages.stream().filter(page -> null != pageIdWithPageInstanceId.get(page.get("PageID"))).forEach( page -> {
tt.execute(page, userInstanceId);
});
}

Hashmap value changed after requesting from session

I work on Java/HTML project. I've set a hashmap as a session attribute. I request the hashmap from session and put key/value in it
map.put("some string", "1")
. When this action is performed the second time, I print the hashmap content and the only value, that was '1' after the last operation on the hashmap, becomes '-1'.
Hashmap is the best data structure, in my opinion, for this project. Can anyone help?
public class Cart {
private HashMap<String, asd> list;
public Cart(){
list = new HashMap<String, asd>();
}
public HashMap<String, asd> getMap(){
return list;
}
/*
* Parameter: code
* -1: increase quantity by 1
* 0: delete product from the product list
* else: set the product quantity to the passed value
*/
public void alterProduct(int code, String product){
if(list.containsKey(product)) {
if(code == -1) plusOne(product);
if(code == 0) remove(product);
else setValue(product, code);
}else {
asd asd = new asd();
asd.a = 1;
list.put(product, asd);
}
}
private void plusOne(String product){
asd asd = list.get(product);
asd.a = asd.a + 1;
list.put(product, asd);
}
private void remove(String product){
list.remove(product);
}
private void setValue(String product, int code){
asd asd = new asd();
asd.a = code;
list.put(product, asd);
}
}
class asd{
int a;
public String toString(){
return ""+a;
}
}
JSP code where I set Cart object as session attribute:
<%
Cart myCart = new Cart();
session.setAttribute("cart",myCart);
%>
Servlet code:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
Cart cart = (Cart) request.getSession().getAttribute("cart");
cart.alterProduct(-1, (String) request.getSession().getAttribute("name"));
doGet(request, response);
}
After I call alterProduct the second time for the same (String) request.getSession().getAttribute("name") the hashmap value for the same key is '-1'.
What is type/value of product? How it is connected to the "cart"?
I guess what's happen that you mess up data types. Another option is you have bug in the Cart.toString() method. I suggest you change the code with "plain" data type and recheck it. If it fails, use your Cart class without messy conversion and debug the code.
You have bug here:
public void alterProduct(int code, String product){
if(list.containsKey(product)) {
if(code == -1) plusOne(product);
if(code == 0) remove(product);
else setValue(product, code);
}
private void setValue(String product, int code){
asd asd = new asd();
asd.a = code;
list.put(product, asd);
}
Consider what happen when you call art.alterProduct(-1, "something") second time.
list.containsKey(product) is true (you use the same product"), code is -1. So
if(code == -1) plusOne(product); is executed as expected.
But then you have something weired
if(code == 0) remove(product);
else setValue(product, code);
code ==0 is evaluated to false, so else instruction is called. You are calling setValue(product, -1)
As you can see above setValue will assign -1 to the asd.a that is observed by you.

Page expired issue with back button and wicket SortableDataProvider and DataTable

I've got an issue with SortableDataProvider and DataTable in wicket.
I've defined my DataTable as such:
IColumn<Column>[] columns = new IColumn[9];
//column values are mapped to the private attributes listed in ColumnImpl.java
columns[0] = new PropertyColumn<Column>(new Model<String>("#"), "columnPosition", "columnPosition");
columns[1] = new PropertyColumn<Column>(new Model<String>("Description"), "description");
columns[2] = new PropertyColumn<Column>(new Model<String>("Type"), "dataType", "dataType");
Adding it to the table:
DataTable<Column> dataTable = new DataTable<Column>("columnsTable", columns, provider, maxRowsPerPage) {
#Override
protected Item<Column> newRowItem(String id, int index, IModel<Column> model) {
return new OddEvenItem<Column>(id, index, model);
}
};
My data provider:
public class ColumnSortableDataProvider extends SortableDataProvider<Column> {
private static final long serialVersionUID = 1L;
private List<Column> list = null;
public ColumnSortableDataProvider(Table table, String sortProperty) {
this.list = Arrays.asList(table.getColumns().toArray(new Column[0]));
setSort(sortProperty, true);
}
public ColumnSortableDataProvider(List<Column> list, String sortProperty) {
this.list = list;
setSort(sortProperty, true);
}
#Override
public Iterator<? extends Column> iterator(int first, int count) {
/*
first - first row of data
count - minimum number of elements to retrieve
So this method returns an iterator capable of iterating over {first, first+count} items
*/
Iterator<Column> iterator = null;
try {
if(getSort() != null) {
Collections.sort(list, new Comparator<Column>() {
private static final long serialVersionUID = 1L;
#Override
public int compare(Column c1, Column c2) {
int result=1;
PropertyModel<Comparable> model1= new PropertyModel<Comparable>(c1, getSort().getProperty());
PropertyModel<Comparable> model2= new PropertyModel<Comparable>(c2, getSort().getProperty());
if(model1.getObject() == null && model2.getObject() == null)
result = 0;
else if(model1.getObject() == null)
result = 1;
else if(model2.getObject() == null)
result = -1;
else
result = ((Comparable)model1.getObject()).compareTo(model2.getObject());
result = getSort().isAscending() ? result : -result;
return result;
}
});
}
if (list.size() > (first+count))
iterator = list.subList(first, first+count).iterator();
else
iterator = list.iterator();
}
catch (Exception e) {
e.printStackTrace();
}
return iterator;
}
The problem is the following:
- I click a column header to sort by that column.
- I navigate to a different page
- I click Back (or Forward if I do the opposite scenario)
- Page has expired.
It'd be nice to generate the page using PageParameters but I somehow need to intercept the sort event to do so.
Any pointers would be greatly appreciated. Thanks a ton!!
I don't know at a quick glance what might be causing this, but in order to help diagnose, you might want to enable debug logging for org.apache.wicket.Session or possibly more of the wicket code.
The retrieval of a page definitely involves calls to a method
public final Page getPage(final String pageMapName, final String path, final int versionNumber)
in this class, and it has some debug logging.
For help with setting up this logging, have a look at How to initialize log4j properly? or at the docs for log4j.

Resources