【问题标题】:Requestfactory clone proxy to new contextRequestfactory 克隆代理到新的上下文
【发布时间】:2016-04-25 17:57:56
【问题描述】:

我正面临 gwt 问题 5794:http://code.google.com/p/google-web-toolkit/issues/detail?id=5794

我看到它有一个 8 个月大的补丁,但它没有包含在 gwt 2.5 RC1 中 http://gwt-code-reviews.appspot.com/1620804/

有谁知道这个补丁是否会包含在 gwt 2.5 rc2 或最终版本中?

如果没有,有人可以向我解释一下解决该问题的最佳方法是什么。

提前致谢。

【问题讨论】:

    标签: gwt requestfactory


    【解决方案1】:

    我从 AbstractRequestContext 的 copyBeanAndCollections 方法中想到了这个。它似乎完成了我想要将代理克隆到具有新上下文的另一个代理的工作。这实质上要求您首先创建一个新代理或使用新上下文编辑代理,但我不再需要使用复制构造函数将属性迭代地复制到新代理。我仍在测试,但我创建了一个简单的 GWTTestCase,它似乎工作正常。

    快速更新

    我解决了请求工厂实体定位器无法解析类型的火灾问题,因此我添加了 stableid 和 version 标记以进行克隆。这可能是个坏主意,当我确定稳定 ID 是否用于对原始对象的实际引用的类类型时,我会更新。我的猜测是它用于解析服务器端的类型。

    public class ProxyUtils {
    
        public static <T extends BaseProxy> AutoBean<T> cloneBeanProperties(final T original, final T clone, final RequestContext toContext) {
    
            AutoBean<T> originalBean = AutoBeanUtils.getAutoBean(original);
            AutoBean<T> cloneBean = AutoBeanUtils.getAutoBean(clone);
    
            return cloneBeanProperties(originalBean, cloneBean, toContext);
        }
    
        /**
         * Shallow-clones an autobean and makes duplicates of the collection types.
         * A regular {@link AutoBean#clone} won't duplicate reference properties.
         */
        public static <T extends BaseProxy> AutoBean<T> cloneBeanProperties(final AutoBean<T> toClone, final AutoBean<T> clone, final RequestContext toContext) {
            // NOTE: This may be a bad idea, i don't know if the STABLE_ID is the type or if it is the
            // actual server side reference to this object. Don't want it to update my original object.
            // I added this back in because I was getting an InstantionException in the locator I am
            // pretty sure this is because request factory server side could not resolve the class type. 
            // Maybe someone could shed some light on this one, if you know what the stable id is really
            // used for. 
            clone.setTag(STABLE_ID, toClone.getTag(STABLE_ID));
            clone.setTag(Constants.VERSION_PROPERTY_B64, toClone.getTag(Constants.VERSION_PROPERTY_B64));
            clone.accept(new AutoBeanVisitor() {
                final Map<String, Object> values = AutoBeanUtils.getAllProperties(toClone);
    
                @Override
                public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, CollectionPropertyContext ctx) {
                    // javac generics bug
                    value = AutoBeanUtils.<Collection<?>, Collection<?>> getAutoBean((Collection<?>) values.get(propertyName));
                    if (value != null) {
                        Collection<Object> collection;
                        if (List.class == ctx.getType()) {
                            collection = new ArrayList<Object>();
                        } else if (Set.class == ctx.getType()) {
                            collection = new HashSet<Object>();
                        } else {
                            // Should not get here if the validator works correctly
                            throw new IllegalArgumentException(ctx.getType().getName());
                        }
    
                        if (isValueType(ctx.getElementType()) || isEntityType(ctx.getElementType())) {
                            /*
                             * Proxies must be edited up-front so that the elements
                             * in the collection have stable identity.
                             */
                            for (Object o : value.as()) {
                                if (o == null) {
                                    collection.add(null);
                                } else {
                                    collection.add(editProxy(toContext, (Class<T>) ctx.getType(), (T) o));
                                }
                            }
                        } else {
                            // For simple values, just copy the values
                            collection.addAll(value.as());
                        }
    
                        ctx.set(collection);
                    }
                    return false;
                }
    
                @Override
                public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, PropertyContext ctx) {
                    value = AutoBeanUtils.getAutoBean(values.get(propertyName));
                    if (value != null) {
                        if (isValueType(ctx.getType()) || isEntityType(ctx.getType())) {
                            /*
                             * Value proxies must be cloned upfront, since the value
                             * is replaced outright.
                             */
                            @SuppressWarnings("unchecked")
                            AutoBean<BaseProxy> valueBean = (AutoBean<BaseProxy>) value;
                            ctx.set(editProxy(toContext, (Class<T>) ctx.getType(), (T) valueBean.as()));
                        } else {
                            ctx.set(value.as());
                        }
                    }
                    return false;
                }
    
                @Override
                public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) {
                    ctx.set(values.get(propertyName));
                    return false;
                }
            });
            return clone;
        }
    
    
    
        /**
         * Take ownership of a proxy instance and make it editable.
         */
        private static <T extends BaseProxy> T editProxy(RequestContext ctx, Class<T> clazz, T object) {
            AutoBean<T> toClone = AutoBeanUtils.getAutoBean(object);
    
            // Create editable copies
            AutoBean<T> parent = toClone;
    
            AutoBean<T> clone = (AutoBean<T>) ctx.create(clazz);
            AutoBean<T> cloned = cloneBeanProperties(toClone, clone, ctx);
    
            cloned.setTag(Constants.PARENT_OBJECT, parent);
            return cloned.as();
        }
    
    
        private static boolean isEntityType(Class<?> clazz) {
            return isAssignableTo(clazz, EntityProxy.class);
        }
    
        private static boolean isValueType(Class<?> clazz) {
            return isAssignableTo(clazz, ValueProxy.class);
        }
    
        public static boolean isAssignableTo(Class<?> thisClass, Class<?> assignableTo ) {
            if(thisClass == null || assignableTo == null) {
              return false;
            }
    
            if(thisClass.equals(assignableTo)) {
                return true;
            }
    
            Class<?> currentSuperClass = thisClass.getSuperclass();
            while(currentSuperClass != null) {
                if(currentSuperClass.equals(assignableTo)) {
                    return true;
                }
                currentSuperClass = thisClass.getSuperclass();
            }
            return false;
        }
    }
    

    这是我成功完成的简单单元测试。我确实有一些错误,我必须在 isAssignableFrom 方法中进行调查。

       public void testCloneProxy() {
            DaoRequestFactory requestFactory = GWT.create(DaoRequestFactory.class);
            RequestContext fromContext = requestFactory.analyticsTaskRequest();
    
            AnalyticsOperationInputProxy from = fromContext.create(AnalyticsOperationInputProxy.class);
    
            from.setDisplayName("DISPLAY 1");
            from.setInputName("INPUT 1");
    
    
            RequestContext toContext = requestFactory.analyticsTaskRequest();
    
            AnalyticsOperationInputProxy to = toContext.create(AnalyticsOperationInputProxy.class);
    
    
            ProxyUtils.cloneBeanProperties(from, to, toContext);
    
            System.out.println("Cloned output " + to.getDisplayName());
            System.out.println("Cloned output " + to.getInputName());
    
            Assert.assertTrue("Display name not equal" , from.getDisplayName().equals(to.getDisplayName()));
            Assert.assertTrue("Input name not equal" , from.getInputName().equals(to.getInputName()));
    
        }
    

    【讨论】:

    • 嗨!由于 STABLE_ID,我发现了一个问题。我在 Jorge P. 的回答中添加了评论。
    【解决方案2】:

    好吧,我认为 Chris Hinshaw 编写了一个很棒的代码来克隆代理,但我将更改一行以使其更好,避免第 129 行出现无限循环

    public class ProxyUtils {
    
    public static <T extends BaseProxy> AutoBean<T> cloneBeanProperties(final T original, final T clone, final RequestContext toContext) {
    
        AutoBean<T> originalBean = AutoBeanUtils.getAutoBean(original);
        AutoBean<T> cloneBean = AutoBeanUtils.getAutoBean(clone);
    
        return cloneBeanProperties(originalBean, cloneBean, toContext);
    }
    
    /**
     * Shallow-clones an autobean and makes duplicates of the collection types.
     * A regular {@link AutoBean#clone} won't duplicate reference properties.
     */
    public static <T extends BaseProxy> AutoBean<T> cloneBeanProperties(final AutoBean<T> toClone, final AutoBean<T> clone, final RequestContext toContext) {
        // NOTE: This may be a bad idea, i don't know if the STABLE_ID is the type or if it is the
        // actual server side reference to this object. Don't want it to update my original object.
        // I added this back in because I was getting an InstantionException in the locator I am
        // pretty sure this is because request factory server side could not resolve the class type. 
        // Maybe someone could shed some light on this one, if you know what the stable id is really
        // used for. 
        clone.setTag(STABLE_ID, clone.getTag(STABLE_ID));
        clone.setTag(Constants.VERSION_PROPERTY_B64, toClone.getTag(Constants.VERSION_PROPERTY_B64));
        clone.accept(new AutoBeanVisitor() {
            final Map<String, Object> values = AutoBeanUtils.getAllProperties(toClone);
    
            @Override
            public boolean visitCollectionProperty(String propertyName, AutoBean<Collection<?>> value, CollectionPropertyContext ctx) {
                // javac generics bug
                value = AutoBeanUtils.<Collection<?>, Collection<?>> getAutoBean((Collection<?>) values.get(propertyName));
                if (value != null) {
                    Collection<Object> collection;
                    if (List.class == ctx.getType()) {
                        collection = new ArrayList<Object>();
                    } else if (Set.class == ctx.getType()) {
                        collection = new HashSet<Object>();
                    } else {
                        // Should not get here if the validator works correctly
                        throw new IllegalArgumentException(ctx.getType().getName());
                    }
    
                    if (isValueType(ctx.getElementType()) || isEntityType(ctx.getElementType())) {
                        /*
                         * Proxies must be edited up-front so that the elements
                         * in the collection have stable identity.
                         */
                        for (Object o : value.as()) {
                            if (o == null) {
                                collection.add(null);
                            } else {
                                collection.add(editProxy(toContext, (Class<T>) ctx.getType(), (T) o));
                            }
                        }
                    } else {
                        // For simple values, just copy the values
                        collection.addAll(value.as());
                    }
    
                    ctx.set(collection);
                }
                return false;
            }
    
            @Override
            public boolean visitReferenceProperty(String propertyName, AutoBean<?> value, PropertyContext ctx) {
                value = AutoBeanUtils.getAutoBean(values.get(propertyName));
                if (value != null) {
                    if (isValueType(ctx.getType()) || isEntityType(ctx.getType())) {
                        /*
                         * Value proxies must be cloned upfront, since the value
                         * is replaced outright.
                         */
                        @SuppressWarnings("unchecked")
                        AutoBean<BaseProxy> valueBean = (AutoBean<BaseProxy>) value;
                        ctx.set(editProxy(toContext, (Class<T>) ctx.getType(), (T) valueBean.as()));
                    } else {
                        ctx.set(value.as());
                    }
                }
                return false;
            }
    
            @Override
            public boolean visitValueProperty(String propertyName, Object value, PropertyContext ctx) {
                ctx.set(values.get(propertyName));
                return false;
            }
        });
        return clone;
    }
    
    
    
    /**
     * Take ownership of a proxy instance and make it editable.
     */
    private static <T extends BaseProxy> T editProxy(RequestContext ctx, Class<T> clazz, T object) {
        AutoBean<T> toClone = AutoBeanUtils.getAutoBean(object);
    
        // Create editable copies
        AutoBean<T> parent = toClone;
    
        AutoBean<T> clone = (AutoBean<T>) ctx.create(clazz);
        AutoBean<T> cloned = cloneBeanProperties(toClone, clone, ctx);
    
        cloned.setTag(Constants.PARENT_OBJECT, parent);
        return cloned.as();
    }
    
    
    private static boolean isEntityType(Class<?> clazz) {
        return isAssignableTo(clazz, EntityProxy.class);
    }
    
    private static boolean isValueType(Class<?> clazz) {
        return isAssignableTo(clazz, ValueProxy.class);
    }
    
    public static boolean isAssignableTo(Class<?> thisClass, Class<?> assignableTo ) {
        if(thisClass == null || assignableTo == null) {
          return false;
        }
    
        if(thisClass.equals(assignableTo)) {
            return true;
        }
    
        Class<?> currentSuperClass = thisClass.getSuperclass();
        if(currentSuperClass != null) {
            if(currentSuperClass.equals(assignableTo)) {
                return true;
            }
        }
        return false;
    }
    

    }

    【讨论】:

    • 嗨!由于使用了 STABLE_ID,我发现了一个错误,这导致克隆的代理列表具有重复的条目。我将行更改为 clone.setTag(Constants.STABLE_ID, clone.getTag(Constants.STABLE_ID));它工作正常
    • @Faliorn 如果我理解正确,您更改了“clone.setTag(STABLE_ID, toClone.getTag(STABLE_ID));”到“clone.setTag(STABLE_ID, clone.getTag(STABLE_ID));”并且不再创建重复的条目。对吗?
    • 是的,没错。 stable_id 似乎将每个实体链接到 json 结构中的数据。数据看起来像 {{..., "C": 1, ... }, {..., "C": 1, ... }} 并且数组是 "identificadores": [ {... , "C": 1, ...}, {...,"C": 1,...}]。更改后,C 得到了两个不同的值(例如,10 和 11),并且实体在服务中被很好地接收。
    【解决方案3】:

    不会的。

    从第一次评论开始(恰好是我自己):

    简而言之:这个补丁还不够,它打破了很多规则(“crossed 流”),并且测试被破坏了。

    如果您无法使用问题中建议的序列化/反序列化解决方法,我相信有一种方法可以使用 AutoBeanVisitor 克隆事物。

    当前的“在给定时间只有一个 RequestContext 可以编辑给定代理(由其 stableId 标识)”真的很烦人,而且不合理(至少不再是;它在请求工厂的早期迭代中)。这是我将来想摆脱的东西,但我们还有一些其他的大事要先做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多