cloneEntity上的java.util.ConcurrentModificationException

时间:2015-06-09 16:29:50

标签: java groovy jspresso

我编写了一个自定义策略,以便使用TrainTimetable的集合克隆实体slots

在模型的摘录下面:

Entity('TrainTimetable') {
    list        'slots'             , ref:'Slot', composition:true
}
Entity('Slot') {
    reference   'trainTimetable'    , ref:'TrainTimetable', reverse:'TrainTimetable-slots'
}

以下方法:

package fr.yc.rail.backend;

import java.util.ArrayList;
import java.util.List;

import org.jspresso.framework.model.entity.IEntity;
import org.jspresso.framework.model.entity.IEntityFactory;
import org.jspresso.framework.model.entity.SmartEntityCloneFactory;

import fr.yc.rail.model.Slot;
import fr.yc.rail.model.TrainTimetable;

public class CloneTrainTimetableFactory extends SmartEntityCloneFactory {

    @Override
    public <E extends IEntity> E cloneEntity(E entityToClone,
            IEntityFactory entityFactory) {

        TrainTimetable clonedTrainTimetable = (TrainTimetable) super.cloneEntity(entityToClone, entityFactory);

        TrainTimetable trainTimetableToClone = (TrainTimetable) entityToClone;

        List<Slot> clonedSlots = new ArrayList<Slot>();

        trainTimetableToClone.getSlots().each {
            Slot clonedSlot = super.cloneEntity(it, entityFactory);
            clonedSlots.add(clonedSlot);
        }

        clonedTrainTimetable.setSlots(clonedSlots);

        return (E) clonedTrainTimetable;
    }
}

调用该方法时,下面的代码会在第二次迭代时引发ConcurrentModificationException错误:

trainTimetableToClone.getSlots().each {
    Slot clonedSlot = super.cloneEntity(it, entityFactory);
    clonedSlots.add(clonedSlot);
}

在堆栈跟踪下方:

ERROR <2015-06-09 18:13:11,193> org.jspresso.framework.application.frontend.controller.AbstractFrontendController : An unexpected error occurred for user demo on session 4379dc1b.
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
    at java.util.ArrayList$Itr.next(Unknown Source)
    at org.hibernate.collection.internal.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:774)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1378)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1372)
    at org.codehaus.groovy.runtime.dgm$149.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at fr.yc.rail.backend.CloneTrainTimetableFactory.cloneEntity(CloneTrainTimetableFactory.groovy:25)
    at org.jspresso.framework.application.backend.action.CloneComponentCollectionAction.cloneElement(CloneComponentCollectionAction.java:61)
    at org.jspresso.framework.application.backend.action.AbstractCloneCollectionAction.getAddedComponents(AbstractCloneCollectionAction.java:71)
    at org.jspresso.framework.application.backend.action.AbstractAddCollectionToMasterAction.execute(AbstractAddCollectionToMasterAction.java:78)
    at org.jspresso.framework.application.backend.AbstractBackendController.execute(AbstractBackendController.java:403)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.executeBackend(AbstractFrontendController.java:1536)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.execute(AbstractFrontendController.java:576)
    at org.jspresso.framework.application.action.AbstractAction.execute(AbstractAction.java:114)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.executeFrontend(AbstractFrontendController.java:1549)
    at org.jspresso.framework.application.frontend.controller.AbstractFrontendController.execute(AbstractFrontendController.java:578)
    at org.jspresso.framework.view.remote.RemoteActionFactory$ActionAdapter.actionPerformed(RemoteActionFactory.java:235)
    at org.jspresso.framework.application.frontend.controller.remote.AbstractRemoteController.handleCommand(AbstractRemoteController.java:440)
    at org.jspresso.framework.application.frontend.controller.remote.AbstractRemoteController.handleCommands(AbstractRemoteController.java:202)
    at org.jspresso.framework.application.startup.remote.RemoteStartup.handleCommands(RemoteStartup.java:88)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at net.sf.qooxdoo.rpc.RemoteCallUtils.callCompatibleMethod(RemoteCallUtils.java:469)
    at net.sf.qooxdoo.rpc.RpcServlet.handleRPC(RpcServlet.java:374)
    at net.sf.qooxdoo.rpc.RpcServlet.doPost(RpcServlet.java:481)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.jspresso.framework.util.http.HttpRequestHolder.doFilter(HttpRequestHolder.java:99)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:421)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Unknown Source)

错误意味着我正在迭代一个正在改变大小的集合,但我不知道如何摆脱它。

1 个答案:

答案 0 :(得分:1)

describe('in EditMeetingCtrl', function () { // ... beforeEach(angular.mock.module('myApp')); beforeEach(angular.mock.module(function ($provide) { // mock the entire $state provider $provide.provider('$state', function () { return { $get: function () { return { // by default it will be an empty object params: {} }; } }; }); })); // .... describe('EditMeetingCtrl.save()', function () { // inject mocked $state - one that you've provided above var $rootScope, scope, $controller, $q, state; // ---------------------------------------^^^ beforeEach(inject(function ($rootScope, $controller, $state, _meetingService) { // ------------------------------------------------^^^ state = $state; })); // ... it("should save the meeting object", function() { // provide custom params which will be used in a call state.params = { companyId: 123, meetingId: 567 }; spyOn(meetingService, 'saveMeeting').and.returnValue(fakeHttpPromise); scope.save(); expect(meetingService.saveMeeting).toHaveBeenCalledWith( meetingId , companyId , meetingObj); }); }); // ... }); TrainTimetable之间有1-N双向关联。这意味着无论何时更新关联的一方,Jspresso都会负责更新另一方以保持模型的一致性,例如: :

  • 如果您在广告位上设置Slot属性,该广告位将添加到列车时刻表的反向trainTimetable集合中。
  • 如果您向列车时刻表的slots属性添加一个广告位,则添加的广告位的slots反向属性将更新为列车时刻表。

在以下代码中:

trainTimetable

由于之前说明的规则, Slot clonedSlot = super.cloneEntity(it, entityFactory); 将被添加到clonedSlot位置。所以迭代的集合实际上会改变。

为了解决问题,在迭代之前复制一个集合就足够了,即:

trainTimeTableToClone