@@ -0,0 +1,280 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils; | |||
import java.io.PrintWriter; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Map.Entry; | |||
import java.util.Set; | |||
import com.google.gwt.core.client.GWT; | |||
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.TreeLogger.Type; | |||
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; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import com.vaadin.shared.communication.ServerRpc; | |||
import com.vaadin.shared.ui.Connect; | |||
import com.vaadin.shared.ui.Connect.LoadStyle; | |||
import com.vaadin.terminal.gwt.client.ServerConnector; | |||
import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader; | |||
import com.vaadin.terminal.gwt.client.metadata.TypeDataBundle; | |||
import com.vaadin.terminal.gwt.client.metadata.TypeDataStore; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorBundle; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.ConnectorInitVisitor; | |||
import com.vaadin.terminal.gwt.widgetsetutils.metadata.TypeVisitor; | |||
public class ConnectorBundleLoaderFactory extends Generator { | |||
@Override | |||
public String generate(TreeLogger logger, GeneratorContext context, | |||
String typeName) throws UnableToCompleteException { | |||
TypeOracle typeOracle = context.getTypeOracle(); | |||
try { | |||
JClassType classType = typeOracle.getType(typeName); | |||
String packageName = classType.getPackage().getName(); | |||
String className = classType.getSimpleSourceName() + "Impl"; | |||
generateClass(logger, context, packageName, className, typeName); | |||
return packageName + "." + className; | |||
} catch (UnableToCompleteException e) { | |||
// Just rethrow | |||
throw e; | |||
} catch (Exception e) { | |||
logger.log(Type.ERROR, getClass() + " failed", e); | |||
throw new UnableToCompleteException(); | |||
} | |||
} | |||
private void generateClass(TreeLogger logger, GeneratorContext context, | |||
String packageName, String className, String requestedType) | |||
throws Exception { | |||
PrintWriter printWriter = context.tryCreate(logger, packageName, | |||
className); | |||
if (printWriter == null) { | |||
return; | |||
} | |||
List<ConnectorBundle> bundles = buildBundles(logger, | |||
context.getTypeOracle()); | |||
ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory( | |||
packageName, className); | |||
composer.setSuperclass(requestedType); | |||
SourceWriter w = composer.createSourceWriter(context, printWriter); | |||
w.println("public void init() {"); | |||
w.indent(); | |||
for (ConnectorBundle bundle : bundles) { | |||
String name = bundle.getName(); | |||
boolean isEager = name | |||
.equals(ConnectorBundleLoader.EAGER_BUNDLE_NAME); | |||
w.print("addAsyncBlockLoader(new AsyncBundleLoader(\""); | |||
w.print(escape(name)); | |||
w.print("\", "); | |||
w.print("new String[] {"); | |||
for (Entry<JClassType, Set<String>> entry : bundle.getIdentifiers() | |||
.entrySet()) { | |||
Set<String> identifiers = entry.getValue(); | |||
for (String id : identifiers) { | |||
w.print("\""); | |||
w.print(escape(id)); | |||
w.print("\","); | |||
} | |||
} | |||
w.println("}) {"); | |||
w.indent(); | |||
w.print("protected void load(final "); | |||
w.print(TypeDataStore.class.getName()); | |||
w.println(" store) {"); | |||
w.indent(); | |||
if (!isEager) { | |||
w.print(GWT.class.getName()); | |||
w.print(".runAsync("); | |||
} | |||
w.print("new "); | |||
w.print(TypeDataBundle.class.getName()); | |||
w.println("(getName()) {"); | |||
w.indent(); | |||
w.println("public void load() {"); | |||
w.indent(); | |||
printBundleData(w, bundle); | |||
// Close load method | |||
w.outdent(); | |||
w.println("}"); | |||
// Close new TypeDataBundle() {} | |||
w.outdent(); | |||
w.print("}"); | |||
if (isEager) { | |||
w.println(".onSuccess();"); | |||
} else { | |||
w.println(");"); | |||
} | |||
// Close load method | |||
w.outdent(); | |||
w.println("}"); | |||
// Close add(new ... | |||
w.outdent(); | |||
w.println("});"); | |||
} | |||
w.outdent(); | |||
w.println("}"); | |||
w.commit(logger); | |||
} | |||
private void printBundleData(SourceWriter w, ConnectorBundle bundle) { | |||
writeIdentifiers(w, bundle); | |||
writeGwtConstructors(w, bundle); | |||
} | |||
private void writeGwtConstructors(SourceWriter w, ConnectorBundle bundle) { | |||
Set<JClassType> constructors = bundle.getGwtConstructors(); | |||
for (JClassType type : constructors) { | |||
w.print("store.setConstructor("); | |||
printClassLiteral(w, type); | |||
w.print(", new Invoker() {"); | |||
w.indent(); | |||
w.println("public Object invoke(Object target, Object[] params) {"); | |||
w.indent(); | |||
w.print("return "); | |||
w.print(GWT.class.getName()); | |||
w.print(".create("); | |||
printClassLiteral(w, type); | |||
w.println(");"); | |||
w.outdent(); | |||
w.println("}"); | |||
w.outdent(); | |||
w.println("});"); | |||
} | |||
} | |||
private void printClassLiteral(SourceWriter w, JClassType type) { | |||
w.print(type.getQualifiedSourceName()); | |||
w.print(".class"); | |||
} | |||
private void writeIdentifiers(SourceWriter w, ConnectorBundle bundle) { | |||
Map<JClassType, Set<String>> identifiers = bundle.getIdentifiers(); | |||
for (Entry<JClassType, Set<String>> entry : identifiers.entrySet()) { | |||
Set<String> ids = entry.getValue(); | |||
JClassType type = entry.getKey(); | |||
for (String id : ids) { | |||
w.print("store.setClass(\""); | |||
w.print(escape(id)); | |||
w.print("\", "); | |||
printClassLiteral(w, type); | |||
w.println(");"); | |||
} | |||
} | |||
} | |||
private List<ConnectorBundle> buildBundles(TreeLogger logger, | |||
TypeOracle typeOracle) throws NotFoundException { | |||
Map<LoadStyle, Collection<JClassType>> connectorsByLoadStyle = new HashMap<LoadStyle, Collection<JClassType>>(); | |||
for (LoadStyle loadStyle : LoadStyle.values()) { | |||
connectorsByLoadStyle.put(loadStyle, new ArrayList<JClassType>()); | |||
} | |||
JClassType connectorType = typeOracle.getType(ServerConnector.class | |||
.getName()); | |||
JClassType[] subtypes = connectorType.getSubtypes(); | |||
for (JClassType connectorSubtype : subtypes) { | |||
if (!connectorSubtype.isAnnotationPresent(Connect.class)) { | |||
continue; | |||
} | |||
LoadStyle loadStyle = getLoadStyle(connectorSubtype); | |||
if (loadStyle != null) { | |||
connectorsByLoadStyle.get(loadStyle).add(connectorSubtype); | |||
} | |||
} | |||
List<ConnectorBundle> bundles = new ArrayList<ConnectorBundle>(); | |||
Collection<TypeVisitor> visitors = getVisitors(typeOracle); | |||
ConnectorBundle eagerBundle = new ConnectorBundle( | |||
ConnectorBundleLoader.EAGER_BUNDLE_NAME, null); | |||
// Eager connectors and all RPC interfaces are loaded by default | |||
eagerBundle.visitTypes(connectorsByLoadStyle.get(LoadStyle.EAGER), | |||
visitors); | |||
eagerBundle.visitSubTypes( | |||
typeOracle.getType(ClientRpc.class.getName()), visitors); | |||
eagerBundle.visitSubTypes( | |||
typeOracle.getType(ServerRpc.class.getName()), visitors); | |||
bundles.add(eagerBundle); | |||
ConnectorBundle deferredBundle = new ConnectorBundle( | |||
ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, eagerBundle); | |||
deferredBundle.visitTypes( | |||
connectorsByLoadStyle.get(LoadStyle.DEFERRED), visitors); | |||
bundles.add(deferredBundle); | |||
Collection<JClassType> lazy = connectorsByLoadStyle.get(LoadStyle.LAZY); | |||
for (JClassType type : lazy) { | |||
ConnectorBundle bundle = new ConnectorBundle(type.getName(), | |||
deferredBundle); | |||
bundle.visitTypes(Collections.singleton(type), visitors); | |||
bundles.add(bundle); | |||
} | |||
return bundles; | |||
} | |||
private Collection<TypeVisitor> getVisitors(TypeOracle oracle) | |||
throws NotFoundException { | |||
List<TypeVisitor> visitors = Arrays | |||
.<TypeVisitor> asList(new ConnectorInitVisitor()); | |||
for (TypeVisitor typeVisitor : visitors) { | |||
typeVisitor.init(oracle); | |||
} | |||
return visitors; | |||
} | |||
protected LoadStyle getLoadStyle(JClassType connectorType) { | |||
Connect annotation = connectorType.getAnnotation(Connect.class); | |||
return annotation.loadStyle(); | |||
} | |||
} |
@@ -104,7 +104,7 @@ public class GeneratedRpcMethodProviderGenerator extends Generator { | |||
composer.addImport("com.google.gwt.core.client.GWT"); | |||
composer.addImport(RpcMethod.class.getName()); | |||
composer.addImport(ClientRpc.class.getName()); | |||
composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class | |||
composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()); | |||
composer.addImplementedInterface(GeneratedRpcMethodProvider.class | |||
.getName()); |
@@ -125,7 +125,7 @@ public class SerializerGenerator extends Generator { | |||
serializerClassName); | |||
composer.addImport(GWT.class.getName()); | |||
composer.addImport(JSONValue.class.getName()); | |||
composer.addImport(com.vaadin.terminal.gwt.client.communication.Type.class | |||
composer.addImport(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()); | |||
// composer.addImport(JSONObject.class.getName()); | |||
// composer.addImport(VPaintableMap.class.getName()); |
@@ -142,7 +142,7 @@ public class SerializerMapGenerator extends Generator { | |||
.findType(JSONSerializer.class.getName()); | |||
JType[] deserializeParamTypes = new JType[] { | |||
typeOracle | |||
.findType(com.vaadin.terminal.gwt.client.communication.Type.class | |||
.findType(com.vaadin.terminal.gwt.client.metadata.Type.class | |||
.getName()), | |||
typeOracle.findType(JSONValue.class.getName()), | |||
typeOracle.findType(ApplicationConnection.class.getName()) }; |
@@ -0,0 +1,116 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
public class ConnectorBundle { | |||
private final String name; | |||
private final ConnectorBundle previousBundle; | |||
private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>(); | |||
private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>(); | |||
private final Set<JClassType> visitedTypes = new HashSet<JClassType>(); | |||
private final Set<JClassType> visitQueue = new HashSet<JClassType>(); | |||
public ConnectorBundle(String name, ConnectorBundle previousBundle) { | |||
this.name = name; | |||
this.previousBundle = previousBundle; | |||
} | |||
public void setNeedsGwtConstructor(JClassType type) { | |||
if (!needsGwtConstructor(type)) { | |||
needsGwtConstructor.add(type); | |||
} | |||
} | |||
private boolean needsGwtConstructor(JClassType type) { | |||
if (needsGwtConstructor.contains(type)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.needsGwtConstructor(type); | |||
} | |||
} | |||
public void setIdentifier(JClassType type, String identifier) { | |||
if (!hasIdentifier(type, identifier)) { | |||
Set<String> set = identifiers.get(type); | |||
if (set == null) { | |||
set = new HashSet<String>(); | |||
identifiers.put(type, set); | |||
} | |||
set.add(identifier); | |||
} | |||
} | |||
private boolean hasIdentifier(JClassType type, String identifier) { | |||
if (identifiers.containsKey(type) | |||
&& identifiers.get(type).contains(identifier)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.hasIdentifier(type, identifier); | |||
} | |||
} | |||
public ConnectorBundle getPreviousBundle() { | |||
return previousBundle; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public Map<JClassType, Set<String>> getIdentifiers() { | |||
return Collections.unmodifiableMap(identifiers); | |||
} | |||
public Set<JClassType> getGwtConstructors() { | |||
return Collections.unmodifiableSet(needsGwtConstructor); | |||
} | |||
public void visitTypes(Collection<JClassType> types, | |||
Collection<TypeVisitor> visitors) { | |||
for (JClassType type : types) { | |||
if (!isTypeVisited(type)) { | |||
visitQueue.add(type); | |||
} | |||
} | |||
visitQueue(visitors); | |||
} | |||
private boolean isTypeVisited(JClassType type) { | |||
if (visitedTypes.contains(type)) { | |||
return true; | |||
} else { | |||
return previousBundle != null | |||
&& previousBundle.isTypeVisited(type); | |||
} | |||
} | |||
private void visitQueue(Collection<TypeVisitor> visitors) { | |||
while (!visitQueue.isEmpty()) { | |||
JClassType type = visitQueue.iterator().next(); | |||
for (TypeVisitor typeVisitor : visitors) { | |||
typeVisitor.visit(type, this); | |||
} | |||
visitQueue.remove(type); | |||
visitedTypes.add(type); | |||
} | |||
} | |||
public void visitSubTypes(JClassType type, Collection<TypeVisitor> visitors) { | |||
visitTypes(Arrays.asList(type.getSubtypes()), visitors); | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
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.vaadin.shared.ui.Connect; | |||
import com.vaadin.terminal.gwt.client.ServerConnector; | |||
public class ConnectorInitVisitor implements TypeVisitor { | |||
private JClassType serverConnector; | |||
@Override | |||
public void init(TypeOracle oracle) throws NotFoundException { | |||
serverConnector = oracle.getType(ServerConnector.class.getName()); | |||
} | |||
@Override | |||
public void visit(JClassType type, ConnectorBundle bundle) { | |||
Connect connectAnnotation = type.getAnnotation(Connect.class); | |||
if (connectAnnotation != null && serverConnector.isAssignableFrom(type)) { | |||
bundle.setIdentifier(type, connectAnnotation.value() | |||
.getCanonicalName()); | |||
bundle.setNeedsGwtConstructor(type); | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.widgetsetutils.metadata; | |||
import com.google.gwt.core.ext.typeinfo.JClassType; | |||
import com.google.gwt.core.ext.typeinfo.NotFoundException; | |||
import com.google.gwt.core.ext.typeinfo.TypeOracle; | |||
public interface TypeVisitor { | |||
public void init(TypeOracle oracle) throws NotFoundException; | |||
public void visit(JClassType type, ConnectorBundle bundle); | |||
} |
@@ -35,11 +35,6 @@ | |||
<when-type-is class="com.vaadin.terminal.gwt.client.Console" /> | |||
</replace-with> | |||
<generate-with | |||
class="com.vaadin.terminal.gwt.widgetsetutils.EagerWidgetMapGenerator"> | |||
<when-type-is class="com.vaadin.terminal.gwt.client.WidgetMap" /> | |||
</generate-with> | |||
<generate-with | |||
class="com.vaadin.terminal.gwt.widgetsetutils.AcceptCriteriaFactoryGenerator"> | |||
<when-type-is | |||
@@ -78,6 +73,10 @@ | |||
<when-type-assignable | |||
class="com.vaadin.terminal.gwt.client.ui.ConnectorStateFactory" /> | |||
</generate-with> | |||
<generate-with class="com.vaadin.terminal.gwt.widgetsetutils.ConnectorBundleLoaderFactory"> | |||
<when-type-assignable class="com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader" /> | |||
</generate-with> | |||
<!-- Use the new cross site linker to get a nocache.js without document.write --> | |||
<add-linker name="xsiframe" /> |
@@ -29,9 +29,11 @@ import com.google.gwt.core.client.JsArrayString; | |||
import com.google.gwt.core.client.Scheduler; | |||
import com.google.gwt.core.client.Scheduler.ScheduledCommand; | |||
import com.google.gwt.user.client.Command; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.Window; | |||
import com.vaadin.shared.ApplicationConstants; | |||
import com.vaadin.terminal.gwt.client.metadata.BundleLoadCallback; | |||
import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader; | |||
import com.vaadin.terminal.gwt.client.metadata.TypeData; | |||
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector; | |||
public class ApplicationConfiguration implements EntryPoint { | |||
@@ -207,7 +209,7 @@ public class ApplicationConfiguration implements EntryPoint { | |||
private HashMap<Integer, String> unknownComponents; | |||
private Class<? extends ServerConnector>[] classes = new Class[1024]; | |||
private Map<Integer, Class<? extends ServerConnector>> classes = new HashMap<Integer, Class<? extends ServerConnector>>(); | |||
private boolean browserDetailsSent = false; | |||
private boolean widgetsetVersionSent = false; | |||
@@ -390,12 +392,26 @@ public class ApplicationConfiguration implements EntryPoint { | |||
public Class<? extends ServerConnector> getConnectorClassByEncodedTag( | |||
int tag) { | |||
try { | |||
return classes[tag]; | |||
} catch (Exception e) { | |||
// component was not present in mappings | |||
return UnknownComponentConnector.class; | |||
Class<? extends ServerConnector> type = classes.get(tag); | |||
if (type == null && !classes.containsKey(tag)) { | |||
// Initialize if not already loaded | |||
Integer currentTag = Integer.valueOf(tag); | |||
while (type == null && currentTag != null) { | |||
String serverSideClassNameForTag = getServerSideClassNameForTag(currentTag); | |||
type = (Class<? extends ServerConnector>) TypeData | |||
.getClass(serverSideClassNameForTag); | |||
currentTag = getParentTag(currentTag.intValue()); | |||
} | |||
if (type == null) { | |||
type = UnknownComponentConnector.class; | |||
if (unknownComponents == null) { | |||
unknownComponents = new HashMap<Integer, String>(); | |||
} | |||
unknownComponents.put(tag, getServerSideClassNameForTag(tag)); | |||
} | |||
classes.put(tag, type); | |||
} | |||
return type; | |||
} | |||
public void addComponentInheritanceInfo(ValueMap valueMap) { | |||
@@ -418,13 +434,7 @@ public class ApplicationConfiguration implements EntryPoint { | |||
for (int i = 0; i < keyArray.length(); i++) { | |||
String key = keyArray.get(i).intern(); | |||
int value = valueMap.getInt(key); | |||
classes[value] = widgetSet.getConnectorClassByTag(value, this); | |||
if (classes[value] == UnknownComponentConnector.class) { | |||
if (unknownComponents == null) { | |||
unknownComponents = new HashMap<Integer, String>(); | |||
} | |||
unknownComponents.put(value, key); | |||
} | |||
widgetSet.ensureConnectorLoaded(value, this); | |||
} | |||
} | |||
@@ -466,86 +476,25 @@ public class ApplicationConfiguration implements EntryPoint { | |||
cmd.execute(); | |||
} | |||
callbacks.clear(); | |||
} else if (dependenciesLoading == 0 && deferredWidgetLoader != null) { | |||
deferredWidgetLoader.trigger(); | |||
} else if (dependenciesLoading == 0 | |||
&& !ConnectorBundleLoader.get().isBundleLoaded( | |||
ConnectorBundleLoader.DEFERRED_BUNDLE_NAME)) { | |||
ConnectorBundleLoader.get().loadBundle( | |||
ConnectorBundleLoader.DEFERRED_BUNDLE_NAME, | |||
new BundleLoadCallback() { | |||
@Override | |||
public void loaded() { | |||
// Nothing to do | |||
} | |||
@Override | |||
public void failed(Throwable reason) { | |||
VConsole.error(reason); | |||
} | |||
}); | |||
} | |||
} | |||
/* | |||
* This loop loads widget implementation that should be loaded deferred. | |||
*/ | |||
static class DeferredWidgetLoader extends Timer { | |||
private static final int FREE_LIMIT = 4; | |||
private static final int FREE_CHECK_TIMEOUT = 100; | |||
int communicationFree = 0; | |||
int nextWidgetIndex = 0; | |||
private boolean pending; | |||
public DeferredWidgetLoader() { | |||
schedule(5000); | |||
} | |||
public void trigger() { | |||
if (!pending) { | |||
schedule(FREE_CHECK_TIMEOUT); | |||
} | |||
} | |||
@Override | |||
public void schedule(int delayMillis) { | |||
super.schedule(delayMillis); | |||
pending = true; | |||
} | |||
@Override | |||
public void run() { | |||
pending = false; | |||
if (!isBusy()) { | |||
Class<? extends ServerConnector> nextType = getNextType(); | |||
if (nextType == null) { | |||
// ensured that all widgets are loaded | |||
deferredWidgetLoader = null; | |||
} else { | |||
communicationFree = 0; | |||
widgetSet.loadImplementation(nextType); | |||
} | |||
} else { | |||
schedule(FREE_CHECK_TIMEOUT); | |||
} | |||
} | |||
private Class<? extends ServerConnector> getNextType() { | |||
Class<? extends ServerConnector>[] deferredLoadedConnectors = widgetSet | |||
.getDeferredLoadedConnectors(); | |||
if (deferredLoadedConnectors.length <= nextWidgetIndex) { | |||
return null; | |||
} else { | |||
return deferredLoadedConnectors[nextWidgetIndex++]; | |||
} | |||
} | |||
private boolean isBusy() { | |||
if (dependenciesLoading > 0) { | |||
communicationFree = 0; | |||
return true; | |||
} | |||
for (ApplicationConnection app : runningApplications) { | |||
if (app.hasActiveRequest()) { | |||
// if an UIDL request or widget loading is active, mark as | |||
// busy | |||
communicationFree = 0; | |||
return true; | |||
} | |||
} | |||
communicationFree++; | |||
return communicationFree < FREE_LIMIT; | |||
} | |||
} | |||
private static DeferredWidgetLoader deferredWidgetLoader; | |||
@Override | |||
public void onModuleLoad() { | |||
@@ -582,7 +531,6 @@ public class ApplicationConfiguration implements EntryPoint { | |||
return; | |||
} | |||
registerCallback(GWT.getModuleName()); | |||
deferredWidgetLoader = new DeferredWidgetLoader(); | |||
} | |||
/** |
@@ -67,8 +67,8 @@ import com.vaadin.terminal.gwt.client.communication.JsonEncoder; | |||
import com.vaadin.terminal.gwt.client.communication.RpcManager; | |||
import com.vaadin.terminal.gwt.client.communication.SerializerMap; | |||
import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; | |||
import com.vaadin.terminal.gwt.client.communication.Type; | |||
import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector; | |||
import com.vaadin.terminal.gwt.client.ui.VContextMenu; | |||
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager; |
@@ -18,17 +18,12 @@ package com.vaadin.terminal.gwt.client; | |||
import com.google.gwt.core.client.GWT; | |||
import com.vaadin.terminal.gwt.client.communication.HasJavaScriptConnectorHelper; | |||
import com.vaadin.terminal.gwt.client.metadata.BundleLoadCallback; | |||
import com.vaadin.terminal.gwt.client.metadata.ConnectorBundleLoader; | |||
import com.vaadin.terminal.gwt.client.metadata.TypeData; | |||
import com.vaadin.terminal.gwt.client.ui.UnknownComponentConnector; | |||
public class WidgetSet { | |||
/** | |||
* WidgetSet (and its extensions) delegate instantiation of widgets and | |||
* client-server matching to WidgetMap. The actual implementations are | |||
* generated with gwts generators/deferred binding. | |||
*/ | |||
private WidgetMap widgetMap = GWT.create(WidgetMap.class); | |||
/** | |||
* Create an uninitialized connector that best matches given UIDL. The | |||
* connector must implement {@link ServerConnector}. | |||
@@ -65,7 +60,8 @@ public class WidgetSet { | |||
/* | |||
* let the auto generated code instantiate this type | |||
*/ | |||
ServerConnector connector = widgetMap.instantiate(classType); | |||
ServerConnector connector = (ServerConnector) TypeData.getType( | |||
classType).createInstance(); | |||
if (connector instanceof HasJavaScriptConnectorHelper) { | |||
((HasJavaScriptConnectorHelper) connector) | |||
.getJavascriptConnectorHelper().setTag(tag); | |||
@@ -102,26 +98,32 @@ public class WidgetSet { | |||
* @param applicationConfiguration | |||
* @return | |||
*/ | |||
public Class<? extends ServerConnector> getConnectorClassByTag(int tag, | |||
ApplicationConfiguration conf) { | |||
Class<? extends ServerConnector> connectorClass = null; | |||
public void ensureConnectorLoaded(int tag, ApplicationConfiguration conf) { | |||
ConnectorBundleLoader loader = ConnectorBundleLoader.get(); | |||
String bundleName = null; | |||
Integer t = tag; | |||
do { | |||
String serverSideClassName = conf.getServerSideClassNameForTag(t); | |||
connectorClass = widgetMap | |||
.getConnectorClassForServerSideClassName(serverSideClassName); | |||
t = conf.getParentTag(t); | |||
} while (connectorClass == UnknownComponentConnector.class && t != null); | |||
bundleName = loader.getBundleForIdentifier(serverSideClassName); | |||
return connectorClass; | |||
} | |||
t = conf.getParentTag(t); | |||
} while (bundleName == null && t != null); | |||
public Class<? extends ServerConnector>[] getDeferredLoadedConnectors() { | |||
return widgetMap.getDeferredLoadedConnectors(); | |||
} | |||
if (bundleName != null && !loader.isBundleLoaded(bundleName)) { | |||
ApplicationConfiguration.startDependencyLoading(); | |||
loader.loadBundle(bundleName, new BundleLoadCallback() { | |||
@Override | |||
public void loaded() { | |||
ApplicationConfiguration.endDependencyLoading(); | |||
} | |||
public void loadImplementation(Class<? extends ServerConnector> nextType) { | |||
widgetMap.ensureInstantiator(nextType); | |||
@Override | |||
public void failed(Throwable reason) { | |||
VConsole.error(reason); | |||
ApplicationConfiguration.endDependencyLoading(); | |||
} | |||
}); | |||
} | |||
} | |||
} |
@@ -17,6 +17,7 @@ package com.vaadin.terminal.gwt.client.communication; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
public interface DiffJSONSerializer<T> extends JSONSerializer<T> { | |||
/** |
@@ -20,6 +20,7 @@ import com.google.gwt.json.client.JSONObject; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.ConnectorMap; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
/** | |||
* Implementors of this interface knows how to serialize an Object of a given |
@@ -31,6 +31,7 @@ import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.shared.Connector; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.ConnectorMap; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
/** | |||
* Client side decoder for decodeing shared state and other values from JSON |
@@ -29,6 +29,7 @@ import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.ConnectorMap; | |||
import com.vaadin.terminal.gwt.client.ServerConnector; | |||
import com.vaadin.terminal.gwt.client.VConsole; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
/** | |||
* Client side RPC manager that can invoke methods based on RPC calls received |
@@ -16,6 +16,7 @@ | |||
package com.vaadin.terminal.gwt.client.communication; | |||
import com.vaadin.shared.communication.ClientRpc; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
public abstract class RpcMethod { | |||
private String interfaceName; |
@@ -20,6 +20,7 @@ import com.google.gwt.json.client.JSONObject; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.shared.communication.URLReference; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.metadata.Type; | |||
public class URLReference_Serializer implements JSONSerializer<URLReference> { | |||
@@ -0,0 +1,86 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.List; | |||
public abstract class AsyncBundleLoader { | |||
public enum State { | |||
NOT_STARTED, LOADING, LOADED, ERROR; | |||
} | |||
private State state = State.NOT_STARTED; | |||
private Throwable error = null; | |||
private List<BundleLoadCallback> callbacks = new ArrayList<BundleLoadCallback>(); | |||
private final String packageName; | |||
private final String[] indentifiers; | |||
public AsyncBundleLoader(String packageName, String[] indentifiers) { | |||
this.packageName = packageName; | |||
this.indentifiers = indentifiers; | |||
} | |||
protected abstract void load(TypeDataStore store); | |||
public List<BundleLoadCallback> setError(Throwable error) { | |||
assert state == State.LOADING; | |||
state = State.ERROR; | |||
this.error = error; | |||
return clearCallbacks(); | |||
} | |||
public Throwable getError() { | |||
return error; | |||
} | |||
public State getState() { | |||
return state; | |||
} | |||
public List<BundleLoadCallback> getCallback() { | |||
return Collections.unmodifiableList(callbacks); | |||
} | |||
public void load(BundleLoadCallback callback, TypeDataStore store) { | |||
assert state == State.NOT_STARTED; | |||
state = State.LOADING; | |||
callbacks.add(callback); | |||
load(store); | |||
} | |||
public void addCallback(BundleLoadCallback callback) { | |||
assert state == State.LOADING; | |||
callbacks.add(callback); | |||
} | |||
public List<BundleLoadCallback> setLoaded() { | |||
assert state == State.LOADING; | |||
state = State.LOADED; | |||
return clearCallbacks(); | |||
} | |||
private List<BundleLoadCallback> clearCallbacks() { | |||
List<BundleLoadCallback> callbacks = this.callbacks; | |||
this.callbacks = null; | |||
return callbacks; | |||
} | |||
public String getName() { | |||
return packageName; | |||
} | |||
public String[] getIndentifiers() { | |||
return indentifiers; | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public interface BundleLoadCallback { | |||
public void loaded(); | |||
public void failed(Throwable reason); | |||
} |
@@ -0,0 +1,97 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.google.gwt.core.shared.GWT; | |||
import com.vaadin.terminal.gwt.client.metadata.AsyncBundleLoader.State; | |||
public abstract class ConnectorBundleLoader { | |||
public static final String EAGER_BUNDLE_NAME = "__eager"; | |||
public static final String DEFERRED_BUNDLE_NAME = "__deferred"; | |||
private static ConnectorBundleLoader impl; | |||
private Map<String, AsyncBundleLoader> asyncBlockLoaders = new HashMap<String, AsyncBundleLoader>(); | |||
private Map<String, String> identifierToBundle = new HashMap<String, String>(); | |||
private final TypeDataStore datStore = new TypeDataStore(); | |||
public ConnectorBundleLoader() { | |||
init(); | |||
} | |||
public TypeDataStore getTypeDataStore() { | |||
return datStore; | |||
} | |||
public static ConnectorBundleLoader get() { | |||
if (impl == null) { | |||
impl = GWT.create(ConnectorBundleLoader.class); | |||
} | |||
return impl; | |||
} | |||
public void loadBundle(String packageName, BundleLoadCallback callback) { | |||
AsyncBundleLoader loader = asyncBlockLoaders.get(packageName); | |||
switch (loader.getState()) { | |||
case NOT_STARTED: | |||
loader.load(callback, getTypeDataStore()); | |||
break; | |||
case LOADING: | |||
loader.addCallback(callback); | |||
break; | |||
case LOADED: | |||
callback.loaded(); | |||
break; | |||
case ERROR: | |||
callback.failed(loader.getError()); | |||
} | |||
} | |||
public boolean isBundleLoaded(String bundleName) { | |||
AsyncBundleLoader loader = asyncBlockLoaders.get(bundleName); | |||
if (loader == null) { | |||
throw new IllegalArgumentException("Bundle " + bundleName | |||
+ " not recognized"); | |||
} | |||
return loader.getState() == State.LOADED; | |||
} | |||
public void setLoaded(String packageName) { | |||
List<BundleLoadCallback> callbacks = asyncBlockLoaders.get(packageName) | |||
.setLoaded(); | |||
for (BundleLoadCallback callback : callbacks) { | |||
callback.loaded(); | |||
} | |||
} | |||
public void setLoadFailure(String bundleName, Throwable reason) { | |||
List<BundleLoadCallback> callbacks = asyncBlockLoaders.get(bundleName) | |||
.setError(reason); | |||
for (BundleLoadCallback callback : callbacks) { | |||
callback.failed(reason); | |||
} | |||
} | |||
public String getBundleForIdentifier(String identifier) { | |||
return identifierToBundle.get(identifier); | |||
} | |||
protected void addAsyncBlockLoader(AsyncBundleLoader loader) { | |||
String name = loader.getName(); | |||
asyncBlockLoaders.put(name, loader); | |||
String[] indentifiers = loader.getIndentifiers(); | |||
for (String identifier : indentifiers) { | |||
identifierToBundle.put(identifier, name); | |||
} | |||
} | |||
public abstract void init(); | |||
} |
@@ -0,0 +1,9 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public interface Invoker { | |||
public Object invoke(Object target, Object[] params); | |||
} |
@@ -0,0 +1,54 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public class Method { | |||
private final Type type; | |||
private final String name; | |||
public Method(Type type, String name) { | |||
this.type = type; | |||
this.name = name; | |||
} | |||
public Type getType() { | |||
return type; | |||
} | |||
public String getName() { | |||
return name; | |||
} | |||
public Type getReturnType() { | |||
return TypeDataStore.getReturnType(this); | |||
} | |||
public void invoke(Object target, Object... params) { | |||
TypeDataStore.getInvoker(this).invoke(target, params); | |||
} | |||
public String getSignature() { | |||
return type.toString() + "." + name; | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (obj == this) { | |||
return true; | |||
} else if (obj instanceof Method) { | |||
Method other = (Method) obj; | |||
return other.getSignature().equals(getSignature()); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return getSignature().hashCode(); | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public class Property { | |||
private final Type type; | |||
private final String name; | |||
public Property(Type type, String name) { | |||
this.type = type; | |||
this.name = name; | |||
} | |||
public Object getValue(Object bean) { | |||
return TypeDataStore.getGetter(this).invoke(bean, null); | |||
} | |||
public String getDelegateToWidgetMethod() { | |||
String value = TypeDataStore.getDelegateToWidget(this); | |||
if (value == null) { | |||
return null; | |||
} else if (value.isEmpty()) { | |||
return "set" + Character.toUpperCase(value.charAt(0)) | |||
+ value.substring(1); | |||
} else { | |||
return value; | |||
} | |||
} | |||
public String getSignature() { | |||
return type.toString() + "." + name; | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (this == obj) { | |||
return true; | |||
} else if (obj instanceof Property) { | |||
Property other = (Property) obj; | |||
return getSignature().equals(other.getSignature()); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return getSignature().hashCode(); | |||
} | |||
} |
@@ -0,0 +1,79 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public class Type { | |||
private final String name; | |||
private final Type[] parameterTypes; | |||
public Type(Class<?> clazz) { | |||
name = clazz.getName(); | |||
parameterTypes = null; | |||
} | |||
public Type(String baseTypeName, Type[] parameterTypes) { | |||
name = baseTypeName; | |||
this.parameterTypes = parameterTypes; | |||
} | |||
public String getBaseTypeName() { | |||
return name; | |||
} | |||
public Type[] getParameterTypes() { | |||
return parameterTypes; | |||
} | |||
public Object createInstance() { | |||
Invoker invoker = TypeDataStore.getConstructor(this); | |||
return invoker.invoke(null, null); | |||
} | |||
public Method getMethod(String name) { | |||
return new Method(this, name); | |||
} | |||
public Property getProperty(String propertyName) { | |||
return new Property(this, propertyName); | |||
} | |||
public String getSignature() { | |||
String string = name; | |||
if (parameterTypes != null) { | |||
string += '<'; | |||
for (int i = 0; i < parameterTypes.length; i++) { | |||
if (i != 0) { | |||
string += ','; | |||
} | |||
string += parameterTypes[i].toString(); | |||
} | |||
string += '>'; | |||
} | |||
return string; | |||
} | |||
@Override | |||
public String toString() { | |||
return getSignature(); | |||
} | |||
@Override | |||
public boolean equals(Object obj) { | |||
if (obj == this) { | |||
return true; | |||
} else if (obj instanceof Type) { | |||
Type other = (Type) obj; | |||
return other.getSignature().equals(getSignature()); | |||
} else { | |||
return false; | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return getSignature().hashCode(); | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
public class TypeData { | |||
public static Type getType(Class<?> type) { | |||
return TypeDataStore.getType(type); | |||
} | |||
public static Type getType(String identifier) { | |||
return TypeDataStore.getType(getClass(identifier)); | |||
} | |||
public static Class<?> getClass(String identifier) { | |||
return TypeDataStore.getClass(identifier); | |||
} | |||
} |
@@ -0,0 +1,33 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import com.google.gwt.core.client.RunAsyncCallback; | |||
public abstract class TypeDataBundle implements RunAsyncCallback { | |||
private final String name; | |||
public TypeDataBundle(String name) { | |||
this.name = name; | |||
} | |||
@Override | |||
public void onSuccess() { | |||
ConnectorBundleLoader loader = ConnectorBundleLoader.get(); | |||
load(); | |||
loader.setLoaded(getName()); | |||
} | |||
@Override | |||
public void onFailure(Throwable reason) { | |||
ConnectorBundleLoader.get().setLoadFailure(getName(), reason); | |||
} | |||
public abstract void load(); | |||
public String getName() { | |||
return name; | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
@VaadinApache2LicenseForJavaFiles@ | |||
*/ | |||
package com.vaadin.terminal.gwt.client.metadata; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
public class TypeDataStore { | |||
private static final String CONSTRUCTOR_NAME = "!new"; | |||
private final Map<String, Class<?>> identifiers = new HashMap<String, Class<?>>(); | |||
private final Map<Method, Type> returnTypes = new HashMap<Method, Type>(); | |||
private final Map<Method, Invoker> invokers = new HashMap<Method, Invoker>(); | |||
private final Map<Property, Invoker> getters = new HashMap<Property, Invoker>(); | |||
private final Map<Property, String> delegateToWidget = new HashMap<Property, String>(); | |||
public static TypeDataStore get() { | |||
return ConnectorBundleLoader.get().getTypeDataStore(); | |||
} | |||
public void setClass(String identifier, Class<?> type) { | |||
identifiers.put(identifier, type); | |||
} | |||
public static Class<?> getClass(String identifier) { | |||
return get().identifiers.get(identifier); | |||
} | |||
public static Type getType(Class<?> clazz) { | |||
return new Type(clazz); | |||
} | |||
public static Type getReturnType(Method method) { | |||
return get().returnTypes.get(method); | |||
} | |||
public static Invoker getInvoker(Method method) { | |||
return get().invokers.get(method); | |||
} | |||
public static Invoker getConstructor(Type type) { | |||
return get().invokers.get(new Method(type, CONSTRUCTOR_NAME)); | |||
} | |||
public static Invoker getGetter(Property property) { | |||
return get().getters.get(property); | |||
} | |||
public static String getDelegateToWidget(Property property) { | |||
return get().delegateToWidget.get(property); | |||
} | |||
public void setReturnType(Class<?> type, String methodName, Type returnType) { | |||
returnTypes.put(new Method(getType(type), methodName), returnType); | |||
} | |||
public void setConstructor(Class<?> type, Invoker constructor) { | |||
invokers.put(new Method(getType(type), CONSTRUCTOR_NAME), constructor); | |||
} | |||
} |