German version
Szenario
I created following szenario:
I have controller which handles the navigation of a web application.
Something like that:
@Override
public void onValueChange(ValueChangeEvent<String> event) {
String token = event.getValue();
IPresenter presenter = null;
if (token != null) {
if (token.equals("ShowEntry")) {
presenter = new ShowEntryPresenter(new EntryView());
} else if (token.equals("EditEntry")) {
presenter = new EditEntryPresenter(new EditView());
}
if (presenter != null) {
presenter.display();
}
}
}
Because i develop this application in a team with 100 other developers and the application scales very fast I need a solution, that I don't have to edit the controller every time i've got a new presenter. I want that to by dynamic.
Something like that:
@Override
public void onValueChange(ValueChangeEvent<String> event) {
String token = event.getValue();
IPresenter presenter = null;
if (token != null) {
if (token.equals("ShowEntry")) {
presenter = new ShowEntryPresenter(new EntryView());
} else if (token.equals("EditEntry")) {
presenter = new EditEntryPresenter(new EditView());
} else {
presenter = Class.forName(token);
}
....
}
The problem: Class.forName() doesn't work on GWT-Client-side.
To solve this problem you can use a generator to generate the java-script code dynamically.
} else {
PresenterFactory factory = (PresenterFactory) GWT.create(IReflectiveFactory.class);
presenter = (IPresenter) factory.newInstance(
"com.samuelschmid.dynamicpresenters.client." + token);
}
Solution
To make this work, you have to do some stuff:
1. You need a marker interface
2. You need a wrapper interface
3. You need a generator interface
4. You need a factory interface
5. You need a generator class
6. Apply generator to gwt.xml
7. Complie without runtime class casting
1. Marker interface IDynamicPresenter.java
An interface to mark the class which can be loaded dynamically from server to client.
package com.samuelschmid.dynamicpresenters.client
public interface IDynamicPresenter extends IPresenter {
}
2. Wrapper interface IFactoryWrapper.java
To mark the class which should be generated with the new generator class
package com.samuelschmid.dynamicpresenters.client
public interface IFactoryWrapper {
}
3. Generator interface IReflectiveFactory.java
package com.samuelschmid.dynamicpresenters.client
public interface IReflectiveFactory implements IFactoryWrapper{
}
package com.samuelschmid.dynamicpresenters.client
public interface IPresenterFactory {
IDynamicPresenter newInstance(String className);
}
5. Generator class PresenterGenerator.java
package com.samuelschmid.dynamicpresenters.server;
import java.io.PrintWriter;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
public class PresenterGenerator extends Generator {
public String generate(TreeLogger logger, GeneratorContext context, String typeName)
throws UnableToCompleteException {
TypeOracle typeOracle = context.getTypeOracle();
JClassType clazz = typeOracle.findType(typeName);
if (clazz == null) {
throw new UnableToCompleteException();
}
try {
JClassType reflectableType = typeOracle.getType(
"com.samuelschmid.dynamicpresenters.client.IDynamicPresenter");
SourceWriter sourceWriter = getSourceWriter(clazz, context, logger);
if (sourceWriter != null) {
sourceWriter.println("public "+
reflectableType.getQualifiedSourceName()+
" newInstance(String className) {");
JClassType[] types = typeOracle.getTypes();
int count = 0;
for (int i = 0; i < types.length; i++) {
if (types[i].isInterface() == null
&& types[i].isAssignableTo(reflectableType)) {
if (count == 0) {
sourceWriter.println(" if(\""
+ types[i].getQualifiedSourceName()
+ "\".equals(className)) {"
+ " return new "
+ types[i].getQualifiedSourceName() + "();"
+ "}");
} else {
sourceWriter.println(" else if(\""
+ types[i].getQualifiedSourceName()
+ "\".equals(className)) {"
+ " return new "
+ types[i].getQualifiedSourceName() + "();"
+ "}");
}
count++;
}
}
sourceWriter.println("return null;");
sourceWriter.println("}");
sourceWriter.commit(logger);
logger.log(TreeLogger.INFO, "Done Generating source for "
+ clazz.getName(), null);
return clazz.getQualifiedSourceName() + "Wrapper";
}
} catch (NotFoundException e) {
e.printStackTrace();
}
return null;
}
public SourceWriter getSourceWriter(JClassType classType,
GeneratorContext context, TreeLogger logger) {
String packageName = classType.getPackage().getName();
String simpleName = classType.getSimpleSourceName() + "Wrapper";
ClassSourceFileComposerFactory composer =
new ClassSourceFileComposerFactory(packageName, simpleName);
composer.addImplementedInterface(
"com.samuelschmid.dynamicpresenters.client.IPresenterFactory");
PrintWriter printWriter = context.tryCreate(logger, packageName,simpleName);
if (printWriter == null) {
return null;
} else {
SourceWriter sw = composer.createSourceWriter(context, printWriter);
return sw;
}
}
}
6. *.gwt.xml
Define the generator to be used in your module
...
<generate-with
class="com.samuelschmid.dynamicpresenters.server.PresenterGenerator" >
<when-type-assignable
class="com.samuelschmid.dynamicpresenters.client.IFactoryWrapper" />
</generate-with>
...
7. No runtime class-casting
Last but not least, that this code runs on all browsers, you must disable runtime class casting!
Add -XdisableCastChecking to the compiler arguments.
Final thoughts
I found the solution for this real problem on the following blog:
http://programmatica.blogspot.com/2007/10/classfornamefullyqualifiedclassnamenewi.html
To the fact that this post is 5 years old, i thought it might be good to make a new post - it's still state-of-the-art.
nice article . i am looking for this . i got this . nice stuff here .
AntwortenLöschenthis is so n ice blog i have find out .
AntwortenLöschenNice blog . Really help your information . thanks for this . this is so nice review .
AntwortenLöschen