package org.nocket.page;
import gengui.util.SevereGUIException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.Page;
import org.apache.wicket.PageReference;
import org.apache.wicket.application.IClassResolver;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.page.CouldNotLockPageException;
import org.apache.wicket.page.PageAccessSynchronizer;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.time.Duration;
import org.nocket.NocketSession;
import org.nocket.util.SevereWebException;
/** Helper class */
public final class FactoryHelper {
private final Object view;
public FactoryHelper(Object view) {
if (view == null)
throw new SevereWebException("View specified for response page cannot be null.");
this.view = view;
}
@SuppressWarnings("unchecked")
public DMDWebPage getPageInstance() {
DMDWebPage instance = null;
List<PageReference> pageReferences = NocketSession.get().getPageReferences(view.hashCode());
// Iteriert über alle PageReferences, die zu dem HashCode der View hinterlegt sind.
// Dies ist im Normalfall genau eine PageReference, deshalb ist die Iteration hierüber nicht teuer
for (PageReference pageReference : pageReferences) {
try {
//try to lock failing fast: http://mail-archives.apache.org/mod_mbox/wicket-users/201211.mbox/%3CCANgwjP4xsMKo6kKjVSOOnf_qKvdV+nbhXh8bkZ0R6oZN1BS8YA@mail.gmail.com%3E
PageAccessSynchronizer synchronizer = new PageAccessSynchronizer(Duration.ONE_SECOND);
synchronizer.lockPage(pageReference.getPageId());
Page page = pageReference.getPage();
if (view.equals(page.getDefaultModel().getObject())) {
instance = (DMDWebPage) page;
//Das Modelobject muss ein sinnvolles Equals implementieren, dennoch kann sich innerhalb des
//View-Objektes etwas gendert haben, deshalb muss hier Das ModelObject neu gesetzt werden
instance.modelChanging();
((IModel<Object>) instance.getDefaultModel()).setObject(view);
instance.modelChanged();
break;
}
synchronizer.unlockPage(pageReference.getPageId());
} catch (CouldNotLockPageException e) {
//if lock not possible, ignore this page and worst case create a new one
}
}
if (instance == null) {
instance = newPageInstance();
}
storePageForView(instance);
return instance;
}
private DMDWebPage newPageInstance() {
DMDWebPage instance = null;
Class<?> responsePage = null;
try {
responsePage = this.getPageClass();
Constructor<?> constructor = responsePage.getConstructor(IModel.class);
instance = (DMDWebPage) createObject(constructor, createModel(responsePage));
} catch (NoSuchMethodException e) {
String m = "Expected constructor " + responsePage.getSimpleName() + "(IModel<"
+ view.getClass().getSimpleName() + ">) could not be found.";
throw newSevereWebException(m, e);
}
return instance;
}
public Panel getPanelInstance(String id) {
return newPanelInstance(id);
}
private Panel newPanelInstance(String id) {
Object instance = null;
Class<?> responsePanel = null;
try {
responsePanel = this.getPanelClass();
Constructor<?> constructor = responsePanel.getConstructor(String.class, IModel.class);
instance = createObject(constructor, id, new Model((Serializable) view));
} catch (NoSuchMethodException e) {
String m = "Expected constructor " + responsePanel.getSimpleName() + "(IModel<"
+ view.getClass().getSimpleName() + ">) could not be found.";
throw newSevereWebException(m, e);
}
return (Panel) instance;
}
/**
* Erzeugt das Model für eine neue Page. Entweder gibt es in der Page-Klasse
* eine statische Methode mit Namen 'createDefaultModel' oder es wir ein
* Model.of(...) erezugt. Mit der createDefaultModel-Methode gibt es die
* Möglichekeit ein eigenes Model (z.b. ein LoadableDetachableModel) zu
* erzeugen.
*
*/
private IModel<? extends Serializable> createModel(Class<?> responsePage) {
IModel<? extends Serializable> result = null;
try {
Method method = responsePage.getMethod("createDefaultModel", new Class[0]);
if (method != null) {
Object invoke = method.invoke(null, new Object[0]);
result = (IModel<? extends Serializable>) invoke;
}
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
throw new SevereGUIException(e);
} catch (IllegalAccessException e) {
throw new SevereGUIException(e);
} catch (IllegalArgumentException e) {
throw new SevereGUIException(e);
} catch (InvocationTargetException e) {
throw new SevereGUIException(e);
}
if (result == null) {
result = Model.of((Serializable) view);
}
return result;
}
/**
* Legt zum HashCode der View die PageReference der Page in der Session ab.
* Falls es bereits eine PageReference zu der View in der Session gibt, wird
* diese entfernt
*
* @param page
* - the Page (PageReference) to store
*/
public void storePageForView(DMDWebPage page) {
// Remove other PageReferences for the same Object
List<PageReference> pageReferences = NocketSession.get().getPageReferences(view.hashCode());
//use copy to iterate to prevent ConcurrentModificationException
List<PageReference> pageReferencesCopy = new ArrayList<PageReference>(pageReferences);
for (PageReference pageReference : pageReferencesCopy) {
try {
//try to lock failing fast: http://mail-archives.apache.org/mod_mbox/wicket-users/201211.mbox/%3CCANgwjP4xsMKo6kKjVSOOnf_qKvdV+nbhXh8bkZ0R6oZN1BS8YA@mail.gmail.com%3E
PageAccessSynchronizer synchronizer = new PageAccessSynchronizer(Duration.ONE_SECOND);
synchronizer.lockPage(pageReference.getPageId());
if (pageReference.getPage() != null && pageReference.getPage().getDefaultModel() != null
&& view.equals(page.getDefaultModel().getObject())) {
pageReferences.remove(pageReference);
// We have stored only one PageReference for this view, so we can stop here
break;
}
//this should behave like a ReentrantLock
synchronizer.unlockPage(pageReference.getPageId());
} catch (CouldNotLockPageException e) {
//if lock not possible, ignore this page, maybe the next call will remove this reference
}
}
pageReferences.add(page.getPageReference());
}
public Class<DMDWebPage> getPageClass() {
return getPageClass(view.getClass());
}
public static Class<DMDWebPage> getPageClass(Class<?> domainClass) {
Class<DMDWebPage> responsePage = null;
String classNamePageSuffix = getPageClassNameforView(domainClass, "page.");
try {
responsePage = loadPage(domainClass, classNamePageSuffix);
} catch (ClassNotFoundException e) {
String classNameNoSuffix = getPageClassNameforView(domainClass, "");
try {
responsePage = loadPage(domainClass, classNameNoSuffix);
} catch (ClassNotFoundException e1) {
try {
// One last chance: If we have an InMemoryClassResolver installed we
// can create the page class on-the-fly in memory
IClassResolver classResolver = WebApplication.get().getApplicationSettings().getClassResolver();
if (classResolver instanceof InMemoryClassResolver) {
InMemoryClassResolver inMemoryResolver = (InMemoryClassResolver) classResolver;
responsePage = (Class<DMDWebPage>) inMemoryResolver.buildPageClassInMemory(domainClass);
} else {
throw new ClassNotFoundException();
}
} catch (ClassNotFoundException e2) {
String m = "Expected class " + classNamePageSuffix + " or " + classNameNoSuffix
+ " could not be found.";
throw newSevereWebException(domainClass, m, e1);
}
}
}
return responsePage;
}
public Class<Panel> getPanelClass() {
return getPanelClass(view.getClass());
}
public Class<Panel> getPanelClass(Class<?> domainClass) {
Class<Panel> responsePage = null;
String classNamePageSuffix = getPanelClassNameforView(domainClass, "panel.");
try {
responsePage = loadPanel(classNamePageSuffix);
} catch (ClassNotFoundException e) {
String classNameNoSuffix = getPanelClassNameforView(domainClass, "");
try {
responsePage = loadPanel(classNameNoSuffix);
} catch (ClassNotFoundException e1) {
try {
// One last chance: If we have an InMemoryClassResolver installed we
// can create the panel class on-the-fly in memory
IClassResolver classResolver = WebApplication.get().getApplicationSettings().getClassResolver();
if (classResolver instanceof InMemoryClassResolver) {
InMemoryClassResolver inMemoryResolver = (InMemoryClassResolver) classResolver;
responsePage = (Class<Panel>) inMemoryResolver.buildPanelClassInMemory(view.getClass());
} else {
throw new ClassNotFoundException();
}
} catch (ClassNotFoundException e2) {
String m = "Expected class " + classNamePageSuffix + " or " + classNameNoSuffix
+ " could not be found.";
throw newSevereWebException(m, e1);
}
}
}
return responsePage;
}
@SuppressWarnings("unchecked")
private static Class<DMDWebPage> loadPage(Class<?> domainClass, String className) throws ClassNotFoundException {
Class<DMDWebPage> responsePage;
responsePage = (Class<DMDWebPage>) Class.forName(className);
if (!DMDWebPage.class.isAssignableFrom(responsePage))
throw newSevereWebException(domainClass, "Expected class " + className + " must extend DMDWebPage.", null);
return responsePage;
}
@SuppressWarnings("unchecked")
private Class<Panel> loadPanel(String className) throws ClassNotFoundException {
Class<Panel> responsePanel;
responsePanel = (Class<Panel>) Class.forName(className);
if (!Panel.class.isAssignableFrom(responsePanel))
throw newSevereWebException("Expected class " + className + " must extend Panel.", null);
return responsePanel;
}
private Object createObject(Constructor<?> constructor, Object... initargs) {
Object res = null;
try {
res = constructor.newInstance(initargs);
} catch (Exception e) {
throw newSevereWebException(null, e);
}
return res;
}
private static String getPageClassNameforView(Class<?> domainClass, String suffixPackage) {
String srcClassName = domainClass.getName();
String srcSimpleName = domainClass.getSimpleName();
return srcClassName.replaceAll(srcSimpleName + "$", suffixPackage + srcSimpleName + "Page");
}
private static String getPanelClassNameforView(Class<?> domainClass, String suffixPackage) {
String srcClassName = domainClass.getName();
String srcSimpleName = domainClass.getSimpleName();
return srcClassName.replaceAll(srcSimpleName + "$", suffixPackage + srcSimpleName + "Panel");
}
private SevereWebException newSevereWebException(String msg, Throwable e) {
throw newSevereWebException(view.getClass(), msg, e);
}
private static SevereWebException newSevereWebException(Class<?> domainClass, String msg, Throwable e) {
final String m = "Cannot create response page for view class " + domainClass.getCanonicalName() + ". ";
return new SevereWebException((msg == null ? m : m + msg), e);
}
}