All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.slice.jdbc.DistributedJDBCStoreManager Maven / Gradle / Ivy

The newest version!
/*
 * 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.slice.jdbc;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.kernel.ConnectionInfo;
import org.apache.openjpa.jdbc.kernel.JDBCStore;
import org.apache.openjpa.jdbc.kernel.JDBCStoreManager;
import org.apache.openjpa.jdbc.sql.Result;
import org.apache.openjpa.jdbc.sql.ResultSetResult;
import org.apache.openjpa.kernel.FetchConfiguration;
import org.apache.openjpa.kernel.OpenJPAStateManager;
import org.apache.openjpa.kernel.PCState;
import org.apache.openjpa.kernel.QueryLanguages;
import org.apache.openjpa.kernel.Seq;
import org.apache.openjpa.kernel.StoreContext;
import org.apache.openjpa.kernel.StoreManager;
import org.apache.openjpa.kernel.StoreQuery;
import org.apache.openjpa.kernel.exps.ExpressionParser;
import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
import org.apache.openjpa.lib.rop.ResultObjectProvider;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.slice.DistributedConfiguration;
import org.apache.openjpa.slice.DistributedStoreManager;
import org.apache.openjpa.slice.Slice;
import org.apache.openjpa.slice.SliceImplHelper;
import org.apache.openjpa.slice.SliceInfo;
import org.apache.openjpa.slice.SlicePersistence;
import org.apache.openjpa.slice.SliceThread;
import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.StoreException;

/**
 * A Store manager for multiple physical databases referred as slice.
 * This receiver behaves like a Transaction Manager as it implements two-phase
 * commit protocol if all the component slices is XA-complaint. The actions are
 * delegated to the underlying slices. The actions are executed in parallel
 * threads whenever possible such as flushing or query. 
* * @author Pinaki Poddar * */ class DistributedJDBCStoreManager extends JDBCStoreManager implements DistributedStoreManager { private final List _slices; private JDBCStoreManager _master; private final DistributedJDBCConfiguration _conf; private static final Localizer _loc = Localizer.forPackage(DistributedJDBCStoreManager.class); /** * Constructs a set of child StoreManagers each connected to a physical * DataSource. * * The supplied configuration carries multiple URL for underlying physical * slices. The first slice is referred as master and is used to * get Sequence based entity identifiers. */ public DistributedJDBCStoreManager(DistributedJDBCConfiguration conf) { super(); _conf = conf; _slices = new ArrayList<>(); List slices = conf.getSlices(Slice.Status.ACTIVE); Slice masterSlice = conf.getMasterSlice(); for (Slice slice : slices) { SliceStoreManager store = new SliceStoreManager(slice); _slices.add(store); if (slice == masterSlice) { _master = store; } } } @Override public DistributedJDBCConfiguration getConfiguration() { return _conf; } public SliceStoreManager getSlice(int i) { return _slices.get(i); } @Override public SliceStoreManager addSlice(Slice slice) { SliceStoreManager result = new SliceStoreManager(slice); result.setContext(getContext(), (JDBCConfiguration)slice.getConfiguration()); _slices.add(result); return result; } /** * Decides the index of the StoreManager by first looking at the * implementation data. If no implementation data is found, then estimates * targets slices by using additional connection info. If no additional * connection info then calls back to user-defined policy. */ protected SliceInfo findSliceNames(OpenJPAStateManager sm, Object edata) { if (SliceImplHelper.isSliceAssigned(sm)) return SliceImplHelper.getSliceInfo(sm); SliceInfo result = null; PersistenceCapable pc = sm.getPersistenceCapable(); Object ctx = getContext(); if (_conf.isReplicated(sm.getMetaData().getDescribedType())) { result = SliceImplHelper.getSlicesByPolicy(pc, _conf, ctx); } else { String origin = estimateSlice(sm, edata); if (origin == null) { result = SliceImplHelper.getSlicesByPolicy(pc, _conf, ctx); } else { result = new SliceInfo(origin); } } return result; } private void assignSlice(OpenJPAStateManager sm, String hint) { if (_conf.isReplicated(sm.getMetaData().getDescribedType())) { SliceImplHelper.getSlicesByPolicy(sm, _conf, getContext()) .setInto(sm); return; } new SliceInfo(hint).setInto(sm); } /** * The additional edata is used, if possible, to find the StoreManager * managing the given StateManager. If the additional data is unavailable * then return null. * */ private String estimateSlice(OpenJPAStateManager sm, Object edata) { if (edata == null || !(edata instanceof ConnectionInfo)) return null; Result result = ((ConnectionInfo) edata).result; if (result instanceof ResultSetResult) { JDBCStore store = ((ResultSetResult) result).getStore(); for (SliceStoreManager slice : _slices) { if (slice == store) { return slice.getName(); } } } return null; } /** * Selects child StoreManager(s) where the given instance resides. */ private StoreManager selectStore(OpenJPAStateManager sm, Object edata) { String[] targets = findSliceNames(sm, edata).getSlices(); for (String target : targets) { SliceStoreManager slice = lookup(target); if (slice == null) throw new InternalException(_loc.get("wrong-slice", target, sm)); return slice; } return null; } @Override public boolean assignField(OpenJPAStateManager sm, int field, boolean preFlush) { return selectStore(sm, null).assignField(sm, field, preFlush); } @Override public boolean assignObjectId(OpenJPAStateManager sm, boolean preFlush) { return _master.assignObjectId(sm, preFlush); } @Override public void beforeStateChange(OpenJPAStateManager sm, PCState fromState, PCState toState) { _master.beforeStateChange(sm, fromState, toState); } @Override public void beginOptimistic() { for (SliceStoreManager slice : _slices) slice.beginOptimistic(); } @Override public boolean cancelAll() { boolean ret = true; for (SliceStoreManager slice : _slices) ret = slice.cancelAll() & ret; return ret; } @Override public int compareVersion(OpenJPAStateManager sm, Object v1, Object v2) { return selectStore(sm, null).compareVersion(sm, v1, v2); } @Override public Object copyDataStoreId(Object oid, ClassMetaData meta) { return _master.copyDataStoreId(oid, meta); } @Override public ResultObjectProvider executeExtent(ClassMetaData meta, boolean subclasses, FetchConfiguration fetch) { int i = 0; List targets = getTargets(fetch); ResultObjectProvider[] tmp = new ResultObjectProvider[targets.size()]; for (SliceStoreManager slice : targets) { tmp[i++] = slice.executeExtent(meta, subclasses, fetch); } return new MergedResultObjectProvider(tmp); } @Override public boolean exists(OpenJPAStateManager sm, Object edata) { String origin = null; for (SliceStoreManager slice : _slices) { if (slice.exists(sm, edata)) { origin = slice.getName(); break; } } if (origin != null) assignSlice(sm, origin); return origin != null; } /** * Flush the given StateManagers after binning them to respective physical * slices. */ @Override public Collection flush(Collection sms) { Collection exceptions = new ArrayList(); List> futures = new ArrayList<>(); Map subsets = bin(sms, null); Collection remaining = new ArrayList<>(subsets.values()); ExecutorService threadPool = SliceThread.getPool(); for (SliceStoreManager slice : _slices) { StateManagerSet subset = subsets.get(slice.getName()); if (subset.isEmpty()) continue; if (subset.containsReplicated()) { Map oldVersions = cacheVersion(subset.getReplicated()); collectException(slice.flush(subset), exceptions); remaining.remove(subset); rollbackVersion(subset.getReplicated(), oldVersions, remaining); } else { futures.add(threadPool.submit(new Flusher(slice, subset))); } } for (Future future : futures) { try { collectException(future.get(), exceptions); } catch (InterruptedException e) { throw new StoreException(e); } catch (ExecutionException e) { throw new StoreException(e.getCause()); } } return exceptions; } private void collectException(Collection error, Collection holder) { if (!(error == null || error.isEmpty())) { holder.addAll(error); } } @Override public void commit() { for (SliceStoreManager slice : _slices) { slice.commit(); } } @Override public void rollback() { for (SliceStoreManager slice : _slices) { slice.rollback(); } } /** * Collect the current versions of the given StateManagers. */ private Map cacheVersion( List sms) { Map result = new HashMap<>(); for (OpenJPAStateManager sm : sms) result.put(sm, sm.getVersion()); return result; } /** * Sets the version of the given StateManagers from the cached versions. * Provided that the StateManager does not appear in the FlusSets of the * remaining. */ private void rollbackVersion(List sms, Map oldVersions, Collection reminder) { if (reminder.isEmpty()) return; for (OpenJPAStateManager sm : sms) { if (occurs(sm, reminder)) sm.setVersion(oldVersions.get(sm)); } } boolean occurs(OpenJPAStateManager sm, Collection reminder) { for (StateManagerSet set : reminder) if (set.contains(sm)) return true; return false; } /** * Separate the given list of StateManagers in separate lists for each slice * by the associated slice identifier of each StateManager. */ private Map bin(Collection sms, Object edata) { Map subsets = new HashMap<>(); for (SliceStoreManager slice : _slices) { subsets.put(slice.getName(), new StateManagerSet(_conf)); } for (Object x : sms) { OpenJPAStateManager sm = (OpenJPAStateManager) x; String[] targets = findSliceNames(sm, edata).getSlices(); for (String slice : targets) { subsets.get(slice).add(sm); } } return subsets; } @Override public Object getClientConnection() { return _master.getClientConnection(); } @Override public Seq getDataStoreIdSequence(ClassMetaData forClass) { return _master.getDataStoreIdSequence(forClass); } @Override public Class getDataStoreIdType(ClassMetaData meta) { return _master.getDataStoreIdType(meta); } @Override public Class getManagedType(Object oid) { return _master.getManagedType(oid); } @Override public Seq getValueSequence(FieldMetaData forField) { return _master.getValueSequence(forField); } @Override public boolean initialize(OpenJPAStateManager sm, PCState state, FetchConfiguration fetch, Object edata) { if (edata instanceof ConnectionInfo) { String origin = estimateSlice(sm, edata); if (origin != null) { if (lookup(origin).initialize(sm, state, fetch, edata)) { assignSlice(sm, origin); return true; } } } // not a part of Query result load. Look into the slices till found List targets = getTargets(fetch); for (SliceStoreManager slice : targets) { if (slice.initialize(sm, state, fetch, edata)) { assignSlice(sm, slice.getName()); return true; } } return false; } @Override public boolean load(OpenJPAStateManager sm, BitSet fields, FetchConfiguration fetch, int lockLevel, Object edata) { return selectStore(sm, edata).load(sm, fields, fetch, lockLevel, edata); } @Override public Collection loadAll(Collection sms, PCState state, int load, FetchConfiguration fetch, Object edata) { Map subsets = bin(sms, edata); Collection result = new ArrayList(); for (SliceStoreManager slice : _slices) { StateManagerSet subset = subsets.get(slice.getName()); if (subset.isEmpty()) continue; Collection tmp = slice.loadAll(subset, state, load, fetch, edata); if (tmp != null && !tmp.isEmpty()) result.addAll(tmp); } return result; } @Override public Object newDataStoreId(Object oidVal, ClassMetaData meta) { return _master.newDataStoreId(oidVal, meta); } /** * Construct a distributed query to be executed against all the slices. */ @Override public StoreQuery newQuery(String language) { if (QueryLanguages.LANG_SQL.equals(language)) { DistributedSQLStoreQuery ret = new DistributedSQLStoreQuery(this); for (SliceStoreManager slice : _slices) { ret.add(slice.newQuery(language)); } return ret; } ExpressionParser parser = QueryLanguages.parserForLanguage(language); if (parser == null) { throw new UnsupportedOperationException("Language [" + language + "] not supported"); } DistributedStoreQuery ret = new DistributedStoreQuery(this, parser); for (SliceStoreManager slice : _slices) { ret.add(slice.newQuery(language)); } return ret; } @Override public FetchConfiguration newFetchConfiguration() { return new TargetFetchConfiguration(); } /** * Sets the context for this receiver and all its underlying slices. */ @Override public void setContext(StoreContext ctx) { super.setContext(ctx); for (SliceStoreManager store : _slices) { store.setContext(ctx, (JDBCConfiguration)store.getSlice().getConfiguration()); } } private SliceStoreManager lookup(String name) { for (SliceStoreManager slice : _slices) if (slice.getName().equals(name)) return slice; return null; } @Override public boolean syncVersion(OpenJPAStateManager sm, Object edata) { String[] targets = findSliceNames(sm, edata).getSlices(); boolean sync = true; for (String replica : targets) { SliceStoreManager slice = lookup(replica); sync &= slice.syncVersion(sm, edata); } return sync; } @Override protected RefCountConnection connectInternal() throws SQLException { List list = new ArrayList<>(); for (SliceStoreManager slice : _slices) list.add(slice.getConnection()); DistributedConnection con = new DistributedConnection(list); return new RefCountConnection(con); } /** * Gets the list of slices mentioned as * {@link SlicePersistence#HINT_TARGET hint} of the given * {@link FetchConfiguration#getHint(String) fetch configuration}. * * @return all active slices if a) the hint is not specified or b) a null * value or c) a non-String or d) matches no active slice. */ List getTargets(FetchConfiguration fetch) { if (fetch == null) return _slices; Object hint = fetch.getHint(SlicePersistence.HINT_TARGET); if (hint == null || !(hint instanceof String || hint instanceof String[])) return _slices; String[] targetNames = hint instanceof String ? new String[]{hint.toString()} : (String[])hint; List targets = new ArrayList<>(); for (SliceStoreManager slice : _slices) { for (String name : targetNames) { if (slice.getName().equals(name)) { targets.add(slice); } } } if (targets.isEmpty()) return _slices; return targets; } private static class Flusher implements Callable { final SliceStoreManager store; final StateManagerSet toFlush; Flusher(SliceStoreManager store, StateManagerSet toFlush) { this.store = store; this.toFlush = toFlush; } @Override public Collection call() throws Exception { return store.flush(toFlush); } } /** * A specialized, insert-only collection of StateManagers that notes * if any of its member is replicated. * */ private static class StateManagerSet extends HashSet { private static final long serialVersionUID = 1L; private final DistributedConfiguration conf; List replicated; StateManagerSet(DistributedConfiguration conf) { this.conf = conf; } @Override public boolean add(OpenJPAStateManager sm) { boolean isReplicated = conf.isReplicated(sm.getMetaData().getDescribedType()); if (isReplicated) { if (replicated == null) replicated = new ArrayList<>(); replicated.add(sm); } return super.add(sm); } @Override public boolean remove(Object sm) { throw new UnsupportedOperationException(); } boolean containsReplicated() { return replicated != null && !replicated.isEmpty(); } List getReplicated() { return replicated; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy