Here is a subset of the code.  It doesn't run or have the logic that changes
the values.  I have included the ChallengeDao, which highlights what I had
to do to make work correctly.  If I remove the PM.closePersistentManager()
it fails in many different ways.  I can even get a failure about can't
modify multiple entities from different groups in the same transaction, and
I don't even have transactions.

My goal is to have one PersistenceManager per request.  That way I don't
have to re-query the objects from datastore twice, because I have read-only
logic afterwards that packets the data needed for the GWT front end.

Like I said before, I am will to share the entire code base with people that
are working on these issues in datanucleus code, but not to the entire
group.  I am willing to answer any questions you might have.

Thanks,
Jeffrey

public class *PM* {
    private PM() {
    }

    static private PersistenceManagerFactory pmfInstance;
    static private HashMap<Thread, PersistenceManager> pmSet = new
HashMap<Thread, PersistenceManager>();
    static private HashSet<Thread> testThreads = new HashSet<Thread>();
    static private long testId = 0;
    static private HashMap<Object, Object> testObj;


    synchronized static private void createPmfInstance() {
        if (pmfInstance == null) {
            pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
        }
    }

    static public void startTest() {
        testThreads.add(Thread.currentThread());
    }

    static public void endTest() {
        testThreads.remove(Thread.currentThread());
    }

    static public Long createTestId(Object obj) {
        if (!testThreads.contains(Thread.currentThread())) {
            throw new IllegalStateException
                    ("TestId is illegal when PersistenceManagerFactory is
active");
        }
        if (testObj == null) {
            testObj = new HashMap<Object, Object>();
        }

        long newId = testId++;
        while (testObj.containsKey(newId)) {
            newId = testId++;
        }
        testObj.put(newId, obj);
        return newId;
    }

    static public Long setTestId(long id, Object obj) {
        if (!testThreads.contains(Thread.currentThread())) {
            throw new IllegalStateException
                    ("TestId is illegal when PersistenceManagerFactory is
active");
        }
        if (testObj == null) {
            testObj = new HashMap<Object, Object>();
        }

        testObj.put(id, obj);
        return id;
    }

    static public PersistenceManager getPersistenceManager() {
        if (pmfInstance == null) {
            createPmfInstance();
        }

        PersistenceManager pm = pmSet.get(Thread.currentThread());
        if (pm == null) {
            pm = pmfInstance.getPersistenceManager();
            pmSet.put(Thread.currentThread(), pm);
        }

        return pm;
    }

    static public void closePersistenceManager() {
        PersistenceManager pm = pmSet.remove(Thread.currentThread());
        if (pm != null) {
            pm.close();
        }
    }

    static public void rollbackPersistenceManager() {
        org.datanucleus.jdo.JDOPersistenceManager pm =
(JDOPersistenceManager) PM.getPersistenceManager();
        ObjectManager om = pm.getObjectManager();
        om.clearDirty();
        PM.closePersistenceManager();
    }

    static public <T> T getObjectById(Class<T> cls, Object key) {
        Object obj;
        if (testThreads.contains(Thread.currentThread())) {
            obj = testObj.get(key);
        } else {
            obj = getPersistenceManager().getObjectById(cls, key);
        }

        return (T) obj;
    }

    static public <T> Collection<T> getObjectsById(Class<T> cls,
Collection<?> keys) {
        Collection<T> collection = new LinkedList<T>();
        if (testThreads.contains(Thread.currentThread())) {
            if (keys != null && !keys.isEmpty()) {
                for (Object key : keys) {
                    collection.add((T) testObj.get(key));
                }
            }
        } else {
            if (keys != null && !keys.isEmpty()) {
                //TODO this is not optimized for Batch gets - switch to low
level api
                PersistenceManager pm = getPersistenceManager();
                LinkedList<Object> oids = new LinkedList<Object>();
                for (Object key : keys) {
                    oids.addLast(pm.newObjectIdInstance(cls, key));
                }
                collection = pm.getObjectsById(oids);
            }
        }

        return collection;
    }
}

//*********************************************************************************************************************
public class *ChallengeDao* {
    public ChallengeDao() {
    }

    public Challenge newChallenge(PlayerId targetId, PlayerId attackerId,
Troops troops) {
        PM.closePersistenceManager();
        PersistenceManager pm = PM.getPersistenceManager();

        PlayerDao playerDao = SessionUtil.getPlayerDao();

        Player target = playerDao.getPlayer(targetId);

        //This is to prevent the challenge from being created if active
challenge can't be set.
        if (target.getActiveChallengeAgainstId() != null) {
            throw new SlowPlayException("Player is already a target of a
challenge");
        }

        Player attacker = playerDao.getPlayer(attackerId);

        Challenge challenge = new Challenge(target, attacker);
        pm.makePersistent(challenge);

        IntegrityCheck ic = IntegrityCheck.start("New Challenge");

        target.setActiveChallengeAgainst(challenge);
        pm.makePersistent(target);

        PM.closePersistenceManager();
        Logs.Object.info("newChallenge:" + challenge);

        joinChallenge(challenge.getId(), attackerId, troops);

        ic.finish();

        return challenge;
    }

    public ChallengeMove joinChallenge(ChallengeId challengeId, PlayerId
joinerId, Troops troops) {
        if (getChallengeMoveByChallengePlayer(challengeId, joinerId) !=
null) {
            throw new SlowPlayException("Already in battle, can't join
again");
        }

        PersistenceManager pm = PM.getPersistenceManager();

        Challenge challenge = getChallenge(challengeId);
        Player joiner = SessionUtil.getPlayerDao().getPlayer(joinerId);
        ChallengeMove challengeMove = new ChallengeMove(challenge, joiner,
troops);
        pm.makePersistent(challengeMove);

        IntegrityCheck ic = IntegrityCheck.start("Join Challenge");

        challenge.addChallengeMove(challengeMove);
        pm.makePersistent(challenge);

        joiner.addActiveChallengeMove(challengeMove);
        pm.makePersistent(joiner);


        ic.finish();

        PM.closePersistenceManager();
        Logs.Object.info("newChallengeMove:" + challengeMove);
        return challengeMove;
    }

    public void deployTroops(PlayerId targetId, PlayerId joinerId, Troops
troops) {
        PM.closePersistenceManager();
        PersistenceManager pm = PM.getPersistenceManager();

        Challenge challenge = getChallengeByTarget(targetId);

        if (challenge == null) {
            newChallenge(targetId, joinerId, troops);
        } else {
            ChallengeMove challengeMove =
getChallengeMoveByChallengePlayer(challenge.getId(), joinerId);
            if (challengeMove == null) {
                joinChallenge(challenge.getId(), joinerId, troops);
            } else {
                Logs.Object.info("Deployed Troops:" +
challengeMove.getId());
                challengeMove.setTroopsDeployed(troops);
                pm.makePersistent(challengeMove);

                PM.closePersistenceManager();
            }
        }
    }

    public void resolveChallenges() {
        PM.closePersistenceManager();

        List<Challenge> challenges = getChallengesToResolve();
        LinkedList<ChallengeId> challengeIds = new
LinkedList<ChallengeId>();
        for (Challenge challenge : challenges) {
            challengeIds.add(challenge.getId());
        }

        PM.closePersistenceManager();

        Challenge challenge = null;
        try {
            for (ChallengeId challengeId : challengeIds) {
                PM.getPersistenceManager();
                challenge = getChallenge(challengeId);
                challenge.resolve();

                PM.closePersistenceManager();

                Logs.Service.info("Resolved:" + challenge);
            }
        } catch (Exception e) {
            e.printStackTrace();
            Logs.Exception.severe("BadResolve [" + challenge + "]:" + e);
        }

    }
}


//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *Player* {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long key;

    @Persistent
    private Long userKey;
    @Persistent
    private String userName;

    @Persistent
    private Long cityKey;

    @Persistent
    private Long originalFamilyKey;
    @Persistent
    private Long currentFamilyKey;

    //**** Money ****

    @Persistent
    private int money = 0;
    @Persistent
    private int totalMoneyReceived = 0;

    //***** Store ****

    @Persistent
    private int storeX = -1;
    @Persistent
    private int storeY = -1;
    @Persistent
    private String storeName;
    @Persistent
    private String storeImage;

    @Persistent(mappedBy = "target")
    private Set<Convincer> convincers = new HashSet<Convincer>();

    //***** Troops/Challenge ****

    @Persistent
    private int muscle;
    @Persistent
    private int dames;

    @Persistent(mappedBy = "owner")
    private Set<TroopHire> activeTroopHires = new HashSet<TroopHire>();

    @Persistent
    private Long activeChallengeAgainstKey;
    @Persistent(defaultFetchGroup = "true")
    private Set<Long> activeChallengeMoveKeys;


    //**** Stats ****
    @Persistent
    private int respect = 0;
    @Persistent
    private int statsChallengesInCount = 0;
    @Persistent
    private double statsConvinceGained = 0;
    @Persistent
    private int statsPlayersConvinced = 0;
    @Persistent
    private int statsStolenTroops = 0;
    @Persistent
    private int statsLostTroops = 0;
    @Persistent
    private int statsMoneyEarned = 0;
}

//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *Convincer* {
    @SuppressWarnings("unused")
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Player target;
    @Persistent
    private Long targetPlayerKey;
    @Persistent
    private Long convincerPlayerKey;
    @Persistent
    private String convincerUserName;
    @Persistent
    private double convincePoints;
    @Persistent
    private boolean inFamily;
}

//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *TroopHire* implements Comparable<TroopHire> {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    @Persistent
    private Player owner;
    @Persistent
    private int muscle;
    @Persistent
    private int dames;
    @Persistent
    private Date hireTime;
    @Persistent
    private Date availableTime;
}

//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *Family* {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long key;

    @Persistent
    private String name;
    @Persistent
    private Long creatorPlayerKey;
    @Persistent
    private Long topPlayerKey;

    @Persistent(defaultFetchGroup = "true")
    private Set<Long> memberPlayerKeys;
    @Persistent
    private int memberCount;
    @Persistent
    private int largestMemberCount;

    @Persistent
    private Date deathTime;
}

//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *Challenge* {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    public Long key;

    @Persistent
    private int locationKey;
    @Persistent
    private Long targetPlayerKey;
    @Persistent
    private String targetName;
    @Persistent
    private Long defendingFamilyKey;

    @Persistent
    private int defenderCount;
    @Persistent
    private int attackerCount;

    @Persistent(defaultFetchGroup = "true")
    public Set<Long> challengeMoveKeys;
    @NotPersistent
    private Collection<ChallengeMove> challengeMoves;

    @Persistent
    private Date creationTime;
    @Persistent
    private Date lockTime;
    @Persistent
    private Date fightTime;
    @Persistent
    private Date troopAvailableTime;
    @Persistent
    private Date resolveTime;
    @Persistent
    private int badResolveCount = 0;

    @NotPersistent
    private boolean playerConvinced;
}

//*********************************************************************************************************************
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class *ChallengeMove* implements Comparable<ChallengeMove> {
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long key;

    @Persistent
    private Long challengeKey;
    @Persistent
    private int locationKey;
    @Persistent
    private Long targetPlayerKey;
    @Persistent
    private String targetName;

    @Persistent
    private Long playerKey;
    @Persistent
    private String playerName;
    @Persistent
    private Long familyKey;

    @Persistent
    private boolean attack;
    @Persistent
    private int muscle;
    @Persistent
    private int dames;
    @Persistent
    private int respect;
    @Persistent
    private int stolenMuscle;
    @Persistent
    private int stolenDames;
    @Persistent
    private double convinceGain;
    @Persistent
    private boolean convincedTarget;
    @Persistent
    private int moneyEarned;

    @Persistent
    private Date joinTime;
    @Persistent
    private Date resolveTime;
    @Persistent
    private Date fightTime;
    @Persistent
    private Date troopAvailableTime;

    @Transient
    private double ratioBonus;
}









On Mon, Nov 23, 2009 at 11:46 AM, Ikai L (Google) <[email protected]> wrote:

> Can you post a subset of your code? It'd be great for the community to take
> a look at what you are doing and see if there's anything that jumps out at
> us.
>
> On Sat, Nov 21, 2009 at 11:46 PM, Jeffrey Goetsch <[email protected]>wrote:
>
>> I have been having a lot of trouble with JDO objects not storing, or at
>> least not storing some of the data.  This has been extremely frustrating,
>> and have thought about giving up on Appengine a few times, because my
>> current design seems bring out all the bugs in the Datastore/Datanucleus
>> code.  Are other people also having major issues?
>>
>> Currently, my design starts a Data session at the beginning of the
>> request, and then closes at the end of the request.  I figured this should
>> save all the changes that I have made to the Datastore.  What I am finding
>> is that some of the objects are being stored and other objects are not.  I
>> have managed to get most of the code working by open and closing
>> PersistenceManager multiple times during the processing of a request.  This
>> feels very hacky, and introduces other bugs where I have a handle to an
>> object opened in one of the early PersistenceManager, but not in the
>> currently opened one.
>>
>> The project has lots of business logic unit test, which makes sure that
>> all the objects get updated correctly per request.  So, when I discover bad
>> data in the datastore, I know it is a problem with the storing of the
>> objects.  I have made multiple attempts at simplifying the issue to post to
>> this list, but as the code gets simpler the issues seem to go away.  I am
>> willing to share my larger code base with developers that are working on
>> trying to fix these problems, but I don't want make a general post to
>> everyone.
>>
>> Thanks,
>> Jeffrey Goetsch
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "Google App Engine for Java" group.
>> To post to this group, send email to
>> [email protected].
>> To unsubscribe from this group, send email to
>> [email protected]<google-appengine-java%[email protected]>
>> .
>> For more options, visit this group at
>> http://groups.google.com/group/google-appengine-java?hl=.
>>
>
>
>
> --
> Ikai Lan
> Developer Programs Engineer, Google App Engine
>
>  --
> You received this message because you are subscribed to the Google Groups
> "Google App Engine for Java" group.
> To post to this group, send email to
> [email protected].
> To unsubscribe from this group, send email to
> [email protected]<google-appengine-java%[email protected]>
> .
> For more options, visit this group at
> http://groups.google.com/group/google-appengine-java?hl=.
>

--

You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en.


Reply via email to