/******************************************************************************* * Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * James Sutherland (Oracle) - initial API and implementation * * 30/05/2012-2.4 Guy Pelletier * - 354678: Temp classloader is still being used during metadata processing ******************************************************************************/ package org.eclipse.persistence.descriptors.partitioning; import java.util.ArrayList; import java.util.List; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.internal.databaseaccess.Accessor; import org.eclipse.persistence.internal.sessions.AbstractRecord; import org.eclipse.persistence.internal.sessions.AbstractSession; import org.eclipse.persistence.queries.DatabaseQuery; /** * PUBLIC: * RangePartitioningPolicy partitions access to a database cluster by a field value from the object, * such as the object's id, location, or tenant. * Each server is assigned a range of values. * All write or read request for object's with that value are sent to the server. * If a query does not include the field as a parameter, then it can either be sent * to all server's and unioned, or left to the sesion's default behavior. * @author James Sutherland * @since EclipseLink 2.2 */ public class RangePartitioningPolicy extends FieldPartitioningPolicy { protected List<RangePartition> partitions = new ArrayList<RangePartition>(); public RangePartitioningPolicy() { super(); } public RangePartitioningPolicy(String partitionField) { super(partitionField); } public RangePartitioningPolicy(String partitionField, boolean unionUnpartitionableQueries) { super(partitionField, unionUnpartitionableQueries); } public RangePartitioningPolicy(String partitionField, RangePartition... partitions) { this(partitionField); for (RangePartition partition : partitions) { addPartition(partition); } } /** * INTERNAL: * Convert all the class-name-based settings to actual class-based settings. * This method is used when converting a project that has been built with * class names to a project with classes. */ public void convertClassNamesToClasses(ClassLoader classLoader) { for (RangePartition rangePartition : partitions) { rangePartition.convertClassNamesToClasses(classLoader); } } /** * PUBLIC: * Return the range partitions. * Each partition represents a range of value to route to a connection pool. * Range values should not overlap. */ public List<RangePartition> getPartitions() { return partitions; } /** * PUBLIC: * Set the range partitions. * Each partition represents a range of value to route to a connection pool. * Range values should not overlap. */ public void setPartitions(List<RangePartition> partitions) { this.partitions = partitions; } /** * PUBLIC: * Add the range partition. */ public void addPartition(String connectionPool, Comparable startValue, Comparable endValue) { getPartitions().add(new RangePartition(connectionPool, startValue, endValue)); } /** * PUBLIC: * Add the range partition. */ public void addPartition(RangePartition partition) { getPartitions().add(partition); } /** * INTERNAL: * Get a connection from one of the pools in a round robin rotation fashion. */ public List<Accessor> getConnectionsForQuery(AbstractSession session, DatabaseQuery query, AbstractRecord arguments) { Object value = arguments.get(this.partitionField); List<Accessor> accessors = null; if (value == null) { if (this.unionUnpartitionableQueries) { accessors = new ArrayList<Accessor>(this.partitions.size()); } else { return null; } } else { accessors = new ArrayList<Accessor>(1); } int size = this.partitions.size(); for (int index = 0; index < size; index++) { RangePartition partition = this.partitions.get(index); if ((value == null) || partition.isInRange(value)) { if (session.getPlatform().hasPartitioningCallback()) { // UCP support. session.getPlatform().getPartitioningCallback().setPartitionId(index); return null; } accessors.add(getAccessor(partition.getConnectionPool(), session, query, false)); if (value != null) { break; } } } if (accessors.isEmpty()) { return null; } return accessors; } /** * INTERNAL: * Allow for the persist call to assign the partition. */ @Override public void partitionPersist(AbstractSession session, Object object, ClassDescriptor descriptor) { Object value = extractPartitionValueForPersist(session, object, descriptor); if (value == null) { return; } int size = this.partitions.size(); for (int index = 0; index < size; index++) { RangePartition partition = this.partitions.get(index); if (partition.isInRange(value)) { if (session.getPlatform().hasPartitioningCallback()) { // UCP support. session.getPlatform().getPartitioningCallback().setPartitionId(index); } else { getAccessor(partition.getConnectionPool(), session, null, false); } return; } } } }