Functional reference in foreach with more than one arguments - java-8

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);
});
}

Related

need help on Unity: parameter doesn't exist in current Construct Context

I was going to practice how to create Inventory, but it seems it won't read the parameter from the other script. I'm working on my training based on my tutorial, here's how the code goes:
public class ItemCatalogue : MonoBehaviour
{
public Items[] AvailableItems;
public Text DisplayArray;
public void GetItem()
{
Items item = AvailableItems[Random.Range(0, AvailableItems.Length)];
InventoryCatalogue.Instance.AddMaterialToCatalogue(new ItemStack(FoodMaterial, amount));//weird, the parameter doesn't exist
DisplayArray.text = item.name;
}
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
}
And here's the other script:
[System.Serializable]
public class ItemStack
{
public Items FoodMaterial;
public int amount;
public ItemStack(Items FoodMaterial, int amount) //here's the parameter
{
this.FoodMaterial = FoodMaterial;
this.amount = amount;
}
}
i didn't expect anything because i haven't finished the tutorial. anybody know why?
well in
InventoryCatalogue.Instance.AddMaterialToCatalogue(new ItemStack(FoodMaterial, amount));
you call
new ItemStack(FoodMaterial, amount)
with the parameters FoodMaterial, amount but your ItemCatalogue class nor the method GetItem contains any variables/fields/properties with those names.
You rather have to pass some values in there like e.g.
// I don't know what you want to pass in as amount
new ItemStack(item, 1)
so
Items item = AvailableItems[Random.Range(0, AvailableItems.Length)];
InventoryCatalogue.Instance.AddMaterialToCatalogue(new ItemStack(item, 1));

Entity Framework, Code First and Full Text Search

I realize that a lot of questions have been asked relating to full text search and Entity Framework, but I hope this question is a bit different.
I am using Entity Framework, Code First and need to do a full text search. When I need to perform the full text search, I will typically have other criteria/restrictions as well - like skip the first 500 rows, or filter on another column, etc.
I see that this has been handled using table valued functions - see http://sqlblogcasts.com/blogs/simons/archive/2008/12/18/LINQ-to-SQL---Enabling-Fulltext-searching.aspx. And this seems like the right idea.
Unfortunately, table valued functions are not supported until Entity Framework 5.0 (and even then, I believe, they are not supported for Code First).
My real question is what are the suggestions for the best way to handle this, both for Entity Framework 4.3 and Entity Framework 5.0. But to be specific:
Other than dynamic SQL (via System.Data.Entity.DbSet.SqlQuery, for example), are there any options available for Entity Framework 4.3?
If I upgrade to Entity Framework 5.0, is there a way I can use table valued functions with code first?
Thanks,
Eric
Using interceptors introduced in EF6, you could mark the full text search in linq and then replace it in dbcommand as described in http://www.entityframework.info/Home/FullTextSearch:
public class FtsInterceptor : IDbCommandInterceptor
{
private const string FullTextPrefix = "-FTSPREFIX-";
public static string Fts(string search)
{
return string.Format("({0}{1})", FullTextPrefix, search);
}
public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
}
public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
RewriteFullTextQuery(command);
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
}
public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
RewriteFullTextQuery(command);
}
public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
}
public static void RewriteFullTextQuery(DbCommand cmd)
{
string text = cmd.CommandText;
for (int i = 0; i < cmd.Parameters.Count; i++)
{
DbParameter parameter = cmd.Parameters[i];
if (parameter.DbType.In(DbType.String, DbType.AnsiString, DbType.StringFixedLength, DbType.AnsiStringFixedLength))
{
if (parameter.Value == DBNull.Value)
continue;
var value = (string)parameter.Value;
if (value.IndexOf(FullTextPrefix) >= 0)
{
parameter.Size = 4096;
parameter.DbType = DbType.AnsiStringFixedLength;
value = value.Replace(FullTextPrefix, ""); // remove prefix we added n linq query
value = value.Substring(1, value.Length - 2);
// remove %% escaping by linq translator from string.Contains to sql LIKE
parameter.Value = value;
cmd.CommandText = Regex.Replace(text,
string.Format(
#"\[(\w*)\].\[(\w*)\]\s*LIKE\s*#{0}\s?(?:ESCAPE N?'~')",
parameter.ParameterName),
string.Format(#"contains([$1].[$2], #{0})",
parameter.ParameterName));
if (text == cmd.CommandText)
throw new Exception("FTS was not replaced on: " + text);
text = cmd.CommandText;
}
}
}
}
}
static class LanguageExtensions
{
public static bool In<T>(this T source, params T[] list)
{
return (list as IList<T>).Contains(source);
}
}
For example, if you have class Note with FTS-indexed field NoteText:
public class Note
{
public int NoteId { get; set; }
public string NoteText { get; set; }
}
and EF map for it
public class NoteMap : EntityTypeConfiguration<Note>
{
public NoteMap()
{
// Primary Key
HasKey(t => t.NoteId);
}
}
and context for it:
public class MyContext : DbContext
{
static MyContext()
{
DbInterception.Add(new FtsInterceptor());
}
public MyContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public DbSet<Note> Notes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new NoteMap());
}
}
you can have quite simple syntax to FTS query:
class Program
{
static void Main(string[] args)
{
var s = FtsInterceptor.Fts("john");
using (var db = new MyContext("CONNSTRING"))
{
var q = db.Notes.Where(n => n.NoteText.Contains(s));
var result = q.Take(10).ToList();
}
}
}
That will generate SQL like
exec sp_executesql N'SELECT TOP (10)
[Extent1].[NoteId] AS [NoteId],
[Extent1].[NoteText] AS [NoteText]
FROM [NS].[NOTES] AS [Extent1]
WHERE contains([Extent1].[NoteText], #p__linq__0)',N'#p__linq__0 char(4096)',#p__linq__0='(john)
Please notice that you should use local variable and cannot move FTS wrapper inside expression like
var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
I have found that the easiest way to implement this is to setup and configure full-text-search in SQL Server and then use a stored procedure. Pass your arguments to SQL, allow the DB to do its job and return either a complex object or map the results to an entity. You don't necessarily have to have dynamic SQL, but it may be optimal. For example, if you need paging, you could pass in PageNumber and PageSize on every request without the need for dynamic SQL. However, if the number of arguments fluctuates per query, it will be the optimal solution.
As the other guys mentioned, I would say start using Lucene.NET
Lucene has a pretty high learning curve, but I found an wrapper for it called "SimpleLucene", that can be found on CodePlex
Let me quote a couple of codeblocks from the blog to show you how easy it is to use. I've just started to use it, but got the hang of it really fast.
First, get some entities from your repository, or in your case, use Entity Framework
public class Repository
{
public IList<Product> Products {
get {
return new List<Product> {
new Product { Id = 1, Name = "Football" },
new Product { Id = 2, Name = "Coffee Cup"},
new Product { Id = 3, Name = "Nike Trainers"},
new Product { Id = 4, Name = "Apple iPod Nano"},
new Product { Id = 5, Name = "Asus eeePC"},
};
}
}
}
The next thing you want to do is create an index-definition
public class ProductIndexDefinition : IIndexDefinition<Product> {
public Document Convert(Product p) {
var document = new Document();
document.Add(new Field("id", p.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
document.Add(new Field("name", p.Name, Field.Store.YES, Field.Index.ANALYZED));
return document;
}
public Term GetIndex(Product p) {
return new Term("id", p.Id.ToString());
}
}
and create an search index for it.
var writer = new DirectoryIndexWriter(
new DirectoryInfo(#"c:\index"), true);
var service = new IndexService();
service.IndexEntities(writer, Repository().Products, ProductIndexDefinition());
So, you now have an search-able index. The only remaining thing to do is.., searching! You can do pretty amazing things, but it can be as easy as this: (for greater examples see the blog or the documentation on codeplex)
var searcher = new DirectoryIndexSearcher(
new DirectoryInfo(#"c:\index"), true);
var query = new TermQuery(new Term("name", "Football"));
var searchService = new SearchService();
Func<Document, ProductSearchResult> converter = (doc) => {
return new ProductSearchResult {
Id = int.Parse(doc.GetValues("id")[0]),
Name = doc.GetValues("name")[0]
};
};
IList<Product> results = searchService.SearchIndex(searcher, query, converter);
The example here http://www.entityframework.info/Home/FullTextSearch is not complete solution. You will need to look into understand how the full text search works. Imagine you have a search field and the user types 2 words to hit search. The above code will throw an exception. You need to do pre-processing on the search phrase first to pass it to the query by using logical AND or OR.
for example your search phrase is "blah blah2" then you need to convert this into:
var searchTerm = #"\"blah\" AND/OR \"blah2\" ";
Complete solution would be:
value = Regex.Replace(value, #"\s+", " "); //replace multiplespaces
value = Regex.Replace(value, #"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
if (value.Any(Char.IsWhiteSpace))
{
value = PreProcessSearchKey(value);
}
public static string PreProcessSearchKey(string searchKey)
{
var splitedKeyWords = searchKey.Split(null); //split from whitespaces
// string[] addDoubleQuotes = new string[splitedKeyWords.Length];
for (int j = 0; j < splitedKeyWords.Length; j++)
{
splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
}
return string.Join(" AND ", splitedKeyWords);
}
this methods uses AND logic operator. You might pass that as an argument and use the method for both AND or OR operators.
You must escape none-alphanumeric characters otherwise it would throw exception when a user enters alpha numeric characters and you have no server site model level validation in place.
I recently had a similar requirement and ended up writing an IQueryable extension specifically for Microsoft full text index access, its available here IQueryableFreeTextExtensions

Attempting to perform complicated anonymous methods is failing

Update (Stupidity Fail)
So then, in all of my convoluted formula code, I neglected the fundamental principles of C#.
Methods may return a value.
static dynamic Construct<T>(T expression){
return expression;
}
Then just use that, instead of a variable ...
Method = Construct<Action<Context, string, int>>(
(context, key, change) =>
{
context.Saved[key] += change;
Console.WriteLine("{0}'s saved value of {1} was changed by {2}, resulting in {3}",
context.Name, key, change, context.Saved[key]);
}
)
I have a situation where I need to call upon methods that don't exist as compiled methods, but rather need to be able to accept an array of parameters and execute as an anonymous function. I thought I had it worked out, but I am running into an issue with the following..
public static IDictionary<string, Function> Expressions =
new Dictionary<string, Function> {
{
"Increase [X] by value of [Y]",
new Function {
Name = "Increase [X] by [Y]",
Parameters = 2,
Types = new List<Type>{
typeof(Param),
typeof(Param)
},
Method = (Expression<Func<Context, Param, Param, bool>>)
((context, x, y) => {
Console.WriteLine("test"); // this is where I need to do stuff...
})
}
}
};
I am being told that a Method name is expected on this. The problem is that Context will be passed in by the object that takes the function and runs its method, because the Context object cannot be pre-bound (it has to be late bound). So basically I package up the trailing 2 parameters (Param) and (Param) in this case and create a function to execute against them.
The database stores those parameters, and then invokes the method passing in the appropriate Context as the first parameter by using Compile().DynamicInvoke(object[] params).
Can anyone give me a hand here as to why I cannot put any kind of logic in between my { }?
UPDATE
Okay, since I've been told this example is unclear, here is an entire program running start to finish that illustrates what I am trying to accomplish.
public class Program {
static void Main(string[] args) {
// simple object stored in database.
var ctx = new Context {
Name = "Ciel",
Saved = new Dictionary<string, int> {
{ "First", 10 },
{ "Second", 20 }
}
};
// simple object stored in database.
var rule = new Rule {
Equations = new List<Equation> {
new Equation {
Parameters = new List<object>{
"First",
5
},
Name = "Increase [X] by value of [Y]"
}
}
};
// =======================================
// runtime environment!!!
// =======================================
var method = Evaluations.Expressions[rule.Equations[0].Name].Method;
var parameters = rule.Equations[0].Parameters;
// insert the specific context as the first parameter.
parameters.Insert(0, ctx);
method.DynamicInvoke(parameters.ToArray());
Console.ReadLine();
}
}
public class Function {
public string Name { get; set; }
public dynamic Method { get; set; }
}
public class Equation {
public string Name { get; set; }
// these objects will be simple enough to serialize.
public IList<object> Parameters { get; set; }
public Function Function { get; set; }
}
public class Context {
public string Name { get; set; }
// this is a crude example, but it serves the demonstration purposes.
public IDictionary<string, int> Saved { get; set; }
}
public class Rule {
// again, a crude example.
public IList<Equation> Equations { get; set; }
}
public static class Evaluations {
static Action<Context, string, int> expr = (context, key, change) =>
{
context.Saved[key] += change;
Console.WriteLine("{0}'s saved value of {1} was changed by {2}, resulting in {3}",
context.Name, key, change, context.Saved[key]);
};
public static IDictionary<string, Function> Expressions =
new Dictionary<string, Function> {
{
"Increase [X] by value of [Y]",
new Function {
Name = "Increase [X] by [Y]",
Method = expr
}
}
};
}
Four problems:
You're trying to create an expression tree from a lambda expression with a statement body (i.e. braces). C# doesn't allow this - you can only convert a statement lambda into a delegate, not an expression tree
Your lambda body doesn't return a Boolean value
You're trying to call an Expression<Func<Context, Param, Param, bool>> as if it were a method with a bool parameter. It's not at all clear what you're trying to do there.
Even if the third point were valid, I suspect you'd need more brackets.
If you refactor your code to make it a little more readable and manageable, you'll probably be well on your way to solving your problem. Rather than having one mammoth C# statement with a single semicolon, split it up into several lines. Something like this:
public static Dictionary<string, Function> Expressions = getExpressions();
private static Dictionary<string, Function> getExpressions()
{
var method = (Expression<Func<Context, Param, Param, bool>>)
((context, x, y) => {
Console.WriteLine("test"); // this is where I need to do stuff...
})(true);
var func = new Function()
{
Name = "Increase [X] by [Y]",
Parameters = 2,
Types = new List<Type>
{
typeof(Param),
typeof(Param)
},
Method = method
};
var dict = new Dictionary<string, Function>();
dict["Increase [X] by value of [Y]"] = func;
return dict;
}
Note: my syntax could be incorrect, but you get the general idea.

Generating a proxy via Reflection.Emit only works when started with Debugging

A task at university was to implement a simple proxy generator / interceptor mechanism using Reflection.Emit.
I came up with the following program.
It seems to work just fine inside Visual Studio in debug mode [F5] (Debug -> Start Debugging) but crashes most of the time when started without debugging [Ctrl + F5] (Debug -> Start Without Debugging).
What is the difference between these two modes? (I do not refer to Debug <> Release mode).
The issue occurs on multiple machines/setups (Win XP SP3 32bit and 64bit, Windows 7 32bit).
Click for pastebin.
// The proxy generator; I assume that the error is buried along the lines emitting the IL code
public static class ProxyGenerator
{
public static T Create<T>(object obj, IInterception interception)
{
Type type = obj.GetType();
TypeBuilder proxy = DefineProxy(type);
FieldBuilder wrappedField = DefinePrivateField(proxy, "wrappedObject", type);
FieldBuilder interceptionField = DefinePrivateField(proxy, "interception", interception.GetType());
DefineConstructor(proxy, wrappedField, interceptionField);
DefineInterfaceMethods(type, proxy, wrappedField, interceptionField);
return (T) Activator.CreateInstance(proxy.CreateType(), obj, interception);
}
private static TypeBuilder DefineProxy(Type type)
{
var assemblyName = new AssemblyName {Name = "GeneratedProxyAssembly"};
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("GeneratedProxyModule");
return moduleBuilder.DefineType(
type.Name + "Proxy",
type.Attributes,
typeof (object),
type.GetInterfaces());
}
private static FieldBuilder DefinePrivateField(TypeBuilder typeBuilder, string fieldName, Type fieldType)
{
return typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Private);
}
private static void DefineConstructor(TypeBuilder typeBuilder, params FieldBuilder[] parameters)
{
ConstructorBuilder ctor = typeBuilder.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard, parameters.Select(f => f.FieldType).ToArray());
// Emit constructor
ILGenerator g = ctor.GetILGenerator();
// Load "this" pointer and call base constructor
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));
// Store parameters in private fields
for (int i = 0; i < parameters.Length; i++)
{
// Load "this" pointer and parameter and store paramater in private field
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Ldarg, i + 1);
g.Emit(OpCodes.Stfld, parameters[i]);
}
// Return
g.Emit(OpCodes.Ret);
}
private static void DefineInterfaceMethods(Type type, TypeBuilder proxy, FieldInfo wrappedField, FieldInfo interceptionField)
{
// Loop through all interface methods
foreach (MethodInfo interfaceMethod in type.GetInterfaces().SelectMany(i => i.GetMethods()))
{
MethodInfo method = type.GetMethod(interfaceMethod.Name);
MethodBuilder methodBuilder = proxy.DefineMethod(
method.Name,
method.Attributes,
method.ReturnType,
method.GetParameters().Select(p => p.ParameterType).ToArray());
// Emit method
ILGenerator g = methodBuilder.GetILGenerator();
// Intercept before
EmitMethodCallOnMember(g, interceptionField, "Before", false);
// Delegate method call
EmitMethodCallOnMember(g, wrappedField, method.Name, true);
// Intercept after
EmitMethodCallOnMember(g, interceptionField, "After", false);
// Return
g.Emit(OpCodes.Ret);
}
}
private static void EmitMethodCallOnMember(ILGenerator g, FieldInfo field, string methodName, bool delegateParameters)
{
// Load "this" pointer to get address of field
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Ldflda, field);
MethodInfo method = field.FieldType.GetMethod(methodName);
if (delegateParameters)
{
// Load method parameters
for (int i = 0; i < method.GetParameters().Length; i++)
{
g.Emit(OpCodes.Ldarg, i + 1);
}
}
// Emit call
g.Emit(OpCodes.Call, method);
}
}
// Some infrastructure
public interface IInterception
{
void Before();
void After();
}
public class LogInterception : IInterception
{
public void Before()
{
Console.WriteLine("Before ... ");
}
public void After()
{
Console.WriteLine("... After");
}
}
public interface ITest
{
string DoSomething(string s1, string s2);
}
public class Test : ITest
{
public string DoSomething(string s1, string s2)
{
Console.WriteLine("... doing something ...");
return s1 + s2;
}
}
// The test program, expected output is down below
internal class Program
{
internal static void Main(string[] args)
{
var test = new Test();
var proxy = ProxyGenerator.Create<ITest>(test, new LogInterception());
Console.WriteLine(test.DoSomething("Hello", " World"));
Console.WriteLine("----------------------------------------");
Console.WriteLine(proxy.DoSomething("Hello", " World"));
Console.ReadKey();
}
}
Another question: What's the best way to narrow down such issues?
I tried to save the generated assembly to disk and open the resulting dll in Reflector but it appeared to be empty.
As mentioned above, when started in debug mode the program seems to work and prints the following output.
... doing something ...
Hello World
----------------------------------------
Before ...
... doing something ...
... After
Hello World
Thanks for your time.
Try to explicitly set x86 mode on project settings tab.
I got the fatal exception only when run program in x64 or AnyCpu mode.
Ah, I've got it. Replace Ldflda with Ldfld. It works fine even without debugger (I just ran .exe).
Ldflda is for fields you pass into method as parameters with ref or out keyword.

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