Requestfactory клонировать прокси в новый контекст

Я столкнулся с проблемой 5794 gwt: 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 или в финальную версию?

Если нет, не мог бы кто-нибудь объяснить мне, что было бы лучшим решением этой проблемы.

Заранее спасибо.


person Manu    schedule 17.09.2012    source источник


Ответы (3)


Не будет.

Из первого обзора (который я делал сам):

Вкратце: этого патча недостаточно, он нарушает множество правил («пересекающиеся потоки»), и тест не работает.

Если вы не можете использовать обходной путь сериализации / десериализации, предложенный в проблеме, я считаю, что есть способ клонировать вещи с помощью AutoBeanVisitor.

Текущий «только один RequestContext может редактировать данный прокси - идентифицированный его стабильным идентификатором - в данный момент» действительно раздражает и не совсем оправдан (по крайней мере, уже сейчас; это было в ранних итерациях Request Factory). Это то, от чего я хотел бы избавиться в будущем, но у нас есть еще несколько важных дел, которые нужно сделать в первую очередь.

person Thomas Broyer    schedule 17.09.2012

Я пришел к этому из метода copyBeanAndCollections AbstractRequestContext. Кажется, он выполняет ту работу, которую я хочу клонировать прокси на другой прокси с новым контекстом. По сути, это требует, чтобы вы сначала создали новый прокси или отредактировали прокси с использованием нового контекста, но мне больше не нужно использовать конструктор копирования для итеративного копирования свойств в новый прокси. Я все еще тестирую, но я создал простой GWTTestCase, и, похоже, он работает правильно.

Быстрое обновление

Я решил проблему с пожаром, из-за которой локатор объектов фабрики запросов не смог определить тип, поэтому я снова добавил теги stableid и version для клонирования. Это может быть плохой идеей, и я обновлю, когда выясню, используется ли стабильный идентификатор для типа класса фактической ссылки на исходный объект. Я предполагаю, что он используется для разрешения типа на стороне сервера.

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

    }
person Chris Hinshaw    schedule 13.04.2013
comment
Привет! Я обнаружил проблему из-за STABLE_ID. Я добавил комментарий к ответу Хорхе П. - person Faliorn; 25.03.2016

Что ж, я думаю, что Крис Хиншоу создал отличный код для клонирования прокси, но я изменю одну строку, чтобы сделать ее лучше, избегая бесконечного цикла в строке 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;
}

}

person Jorge P.    schedule 24.11.2013
comment
Привет! Я обнаружил ошибку из-за использования STABLE_ID, из-за которой список клонированных прокси-серверов имел повторяющиеся записи. Я изменил строку на clone.setTag (Constants.STABLE_ID, clone.getTag (Constants.STABLE_ID)); и он отлично работает - person Faliorn; 25.03.2016
comment
@Faliorn, если я вас правильно понял, вы изменили clone.setTag (STABLE_ID, toClone.getTag (STABLE_ID)); в clone.setTag (STABLE_ID, clone.getTag (STABLE_ID)); и больше не создавалось повторяющихся входов. Это правильно? - person Jorge P.; 27.03.2016
comment
Да это правильно. Похоже, что stable_id связывает каждую сущность с данными в структуре json. Данные выглядели как {{..., C: 1, ...}, {..., C: 1, ...}}, а массив был identiftificadores: [{..., C: 1,. ..}, {..., C: 1, ...}]. После изменения C получил два разных значения (скажем, 10 и 11), и объекты были приняты службой без проблем. - person Faliorn; 04.04.2016