| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.xmlstore; |
| |
| import java.util.ArrayList; |
| import java.util.BitSet; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.apache.openjpa.abstractstore.AbstractStoreManager; |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.kernel.FetchConfiguration; |
| import org.apache.openjpa.kernel.OpenJPAStateManager; |
| import org.apache.openjpa.kernel.PCState; |
| import org.apache.openjpa.lib.rop.ListResultObjectProvider; |
| import org.apache.openjpa.lib.rop.ResultObjectProvider; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.util.OptimisticException; |
| import org.apache.openjpa.util.StoreException; |
| import serp.util.Numbers; |
| |
| /** |
| * Store manager to a back-end consisting of XML files. This |
| * implementation accesses data through the {@link XMLStore} associated with |
| * its {@link XMLConfiguration}. Configuration instances are shared by all |
| * store managers owned by all brokers created with the same factory. |
| * |
| * @see AbstractStoreManager |
| */ |
| public class XMLStoreManager |
| extends AbstractStoreManager { |
| |
| private XMLConfiguration _conf; |
| private XMLStore _store; |
| |
| // changed data within the current transaction |
| private Collection _updates; |
| private Collection _deletes; |
| |
| protected Collection getUnsupportedOptions() { |
| Collection c = super.getUnsupportedOptions(); |
| |
| // remove options we do support but the abstract store doesn't |
| c.remove(OpenJPAConfiguration.OPTION_ID_DATASTORE); |
| c.remove(OpenJPAConfiguration.OPTION_OPTIMISTIC); |
| |
| // and add some that we don't support but the abstract store does |
| c.add(OpenJPAConfiguration.OPTION_EMBEDDED_RELATION); |
| c.add(OpenJPAConfiguration.OPTION_EMBEDDED_COLLECTION_RELATION); |
| c.add(OpenJPAConfiguration.OPTION_EMBEDDED_MAP_RELATION); |
| return c; |
| } |
| |
| protected OpenJPAConfiguration newConfiguration() { |
| // override to use our configuration type |
| return new XMLConfiguration(); |
| } |
| |
| protected void open() { |
| // cache operational state |
| _conf = (XMLConfiguration) ctx.getConfiguration(); |
| _store = _conf.getStore(); |
| } |
| |
| public boolean exists(OpenJPAStateManager sm, Object context) { |
| // see if the given object exists in the store |
| return _store.getData(sm.getMetaData(), sm.getObjectId()) != null; |
| } |
| |
| /** |
| * Increment the version indicator in the given state manager. |
| */ |
| private static void incrementVersion(OpenJPAStateManager sm) { |
| long version = 0; |
| if (sm.getVersion() != null) |
| version = ((Long) sm.getVersion()).longValue() + 1; |
| sm.setNextVersion(Numbers.valueOf(version)); |
| } |
| |
| public boolean initialize(OpenJPAStateManager sm, PCState state, |
| FetchConfiguration fetch, Object context) { |
| // we may already have looked up the backing ObjectData (see our extent |
| // implementation below), and passed it through as the context; if |
| // not, then look it up in the store |
| ObjectData data; |
| if (context != null) |
| data = (ObjectData) context; |
| else |
| data = _store.getData(sm.getMetaData(), sm.getObjectId()); |
| |
| // no matching record? |
| if (data == null) |
| return false; |
| |
| // initialize the state manager with a new instance of the right |
| // type and lifecycle state |
| sm.initialize(data.getMetaData().getDescribedType(), state); |
| |
| // load the data from the ObjectData into the state mgr; note that |
| // this store manager doesn't do any locking -- it relies on the |
| // system's lock manager to lock after the load is complete |
| data.load(sm, fetch); |
| return true; |
| } |
| |
| public boolean load(OpenJPAStateManager sm, BitSet fields, |
| FetchConfiguration fetch, int lockLevel, Object context) { |
| // we may already have looked up the backing ObjectData (see our extent |
| // implementation below), and passed it through as the context; if |
| // not, then look it up in the store |
| ObjectData data; |
| if (context != null) |
| data = (ObjectData) context; |
| else |
| data = _store.getData(sm.getMetaData(), sm.getObjectId()); |
| |
| // no matching record? |
| if (data == null) |
| return false; |
| |
| // load the data from the ObjectData into the state mgr; note that |
| // this store manager doesn't do any locking -- it relies on the |
| // system's lock manager to lock after the load is complete |
| data.load(sm, fields, fetch); |
| return true; |
| } |
| |
| public boolean syncVersion(OpenJPAStateManager sm, Object context) { |
| if (sm.getVersion() == null) |
| return false; |
| |
| // we may already have looked up the backing ObjectData (see our extent |
| // implementation below), and passed it through as the context; if |
| // not, then look it up in the store |
| ObjectData data; |
| if (context != null) |
| data = (ObjectData) context; |
| else |
| data = _store.getData(sm.getMetaData(), sm.getObjectId()); |
| |
| // no record? |
| if (data == null) |
| return false; |
| |
| // if the version of data held by the state mgr is the same as the |
| // version in the datastore, return true, letting the broker know that |
| // it doesn't need to load any more data |
| if (sm.getVersion().equals(data.getVersion())) |
| return true; |
| |
| // set the version to be up-to-date, and return false letting |
| // the broker know that it needs to load up-to-date data |
| sm.setVersion(data.getVersion()); |
| return false; |
| } |
| |
| public void begin() { |
| _store.beginTransaction(); |
| } |
| |
| public void commit() { |
| try { |
| _store.endTransaction(_updates, _deletes); |
| } finally { |
| _updates = null; |
| _deletes = null; |
| } |
| } |
| |
| public void rollback() { |
| _updates = null; |
| _deletes = null; |
| _store.endTransaction(null, null); |
| } |
| |
| protected Collection flush(Collection pNew, Collection pNewUpdated, |
| Collection pNewFlushedDeleted, Collection pDirty, Collection pDeleted) { |
| // we don't support incremental flushing, so pNewUpdated and |
| // pNewFlushedDeleted should be empty; we ignore them here |
| |
| // track optimistic violations |
| Collection exceps = new LinkedList(); |
| |
| // convert instances to ObjectDatas |
| _updates = new ArrayList(pNew.size() + pDirty.size()); |
| _deletes = new ArrayList(pDeleted.size()); |
| |
| // convert additions |
| for (Iterator itr = pNew.iterator(); itr.hasNext();) { |
| // create new object data for instance |
| OpenJPAStateManager sm = (OpenJPAStateManager) itr.next(); |
| Object oid = sm.getObjectId(); |
| ObjectData data = _store.getData(sm.getMetaData(), oid); |
| if (data != null) |
| throw new StoreException("Attempt to insert " |
| + "new object " + sm.getManagedInstance() |
| + "with the same oid as an existing instance: " + oid). |
| setFatal(true); |
| |
| data = new ObjectData(oid, sm.getMetaData()); |
| incrementVersion(sm); |
| data.store(sm); |
| _updates.add(data); |
| } |
| |
| // convert updates |
| for (Iterator itr = pDirty.iterator(); itr.hasNext();) { |
| OpenJPAStateManager sm = (OpenJPAStateManager) itr.next(); |
| ObjectData data = _store.getData(sm.getMetaData(), |
| sm.getObjectId()); |
| |
| // if data has been deleted or has the wrong version, record |
| // opt lock violation |
| if (data == null || !data.getVersion().equals(sm.getVersion())) { |
| exceps.add(new OptimisticException |
| (sm.getManagedInstance())); |
| continue; |
| } |
| |
| // store changes |
| incrementVersion(sm); |
| data = (ObjectData) data.clone(); |
| data.store(sm); |
| _updates.add(data); |
| } |
| |
| // convert deletes |
| for (Iterator itr = pDeleted.iterator(); itr.hasNext();) { |
| OpenJPAStateManager sm = (OpenJPAStateManager) itr.next(); |
| ObjectData data = _store.getData(sm.getMetaData(), |
| sm.getObjectId()); |
| |
| // record delete |
| if (data != null) |
| _deletes.add(data); |
| } |
| |
| return exceps; |
| } |
| |
| public ResultObjectProvider executeExtent(ClassMetaData meta, |
| boolean subclasses, FetchConfiguration fetch) { |
| // ask the store for all ObjectDatas for the given type; this |
| // actually gives us all instances of the base class of the type |
| ObjectData[] datas = _store.getData(meta); |
| Class candidate = meta.getDescribedType(); |
| |
| // create a list of the corresponding persistent objects that |
| // match the type and subclasses criteria |
| List pcs = new ArrayList(datas.length); |
| for (int i = 0; i < datas.length; i++) { |
| // does this instance belong in the extent? |
| Class c = datas[i].getMetaData().getDescribedType(); |
| if (c != candidate && (!subclasses |
| || !candidate.isAssignableFrom(c))) |
| continue; |
| |
| // look up the pc instance for the data, passing in the data |
| // as well so that we can take advantage of the fact that we've |
| // already looked it up. note that in the store manager's |
| // initialize(), load(), etc methods we check for this data |
| // being passed through and save ourselves a trip to the store |
| // if it is present; this is particularly important in systems |
| // where a trip to the store can be expensive. |
| pcs.add(ctx.find(datas[i].getId(), fetch, null, datas[i], 0)); |
| } |
| return new ListResultObjectProvider(pcs); |
| } |
| } |