| /* |
| * 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.datacache; |
| |
| import java.lang.reflect.Array; |
| import java.security.AccessController; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.openjpa.lib.conf.PluginListValue; |
| import org.apache.openjpa.lib.conf.Value; |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.util.UserException; |
| |
| /** |
| * A partitioned data cache maintains a set of partitions that are DataCache themselves. |
| * Each of the partitioned DataCaches can be individually configured. |
| * However, all partitions must be of the same type. By default, this cache uses |
| * {@linkplain ConcurrentDataCache} as its partitions. |
| * <br> |
| * This cache can be configured as a plug-in as follows: |
| * <br> |
| * <code><property name='openjpa.DataCache" |
| * value="partitioned(name=X, PartitionType=concurrent,Partitions='(name=a,cacheSize=100), |
| * (name=b,cacheSize=200)')</code> |
| * <br> |
| * Notice that the individual partition properties are enclosed parentheses, separated by comma |
| * and finally the whole property string is enclosed in single quote. |
| * Each partition must have a non-empty name that are unique among the partitions. |
| * The {@linkplain CacheDistributionPolicy policy} can return |
| * the name of a partition to distribute the managed instances to be cached in respective partition. |
| * |
| * The above configuration will configure a partitioned cache named <code>X</code> with two partitions named |
| * <code>a</code> and <code>b</code> with cache size <code>100</code> and <code>200</code> respectively. |
| * Besides the two partitions, this cache instance itself can store data and referred by its own name |
| * (<code>X</code> in the above example). |
| * <br> |
| * |
| * @author Pinaki Poddar |
| * |
| * @since 2.0.0 |
| */ |
| public class PartitionedDataCache extends ConcurrentDataCache { |
| private static final long serialVersionUID = 1L; |
| private static final Localizer _loc = Localizer.forPackage(PartitionedDataCache.class); |
| private Class<? extends DataCache> _type = ConcurrentDataCache.class; |
| private final List<String> _partProperties = new ArrayList<>(); |
| private final Map<String, DataCache> _partitions = new HashMap<>(); |
| |
| @Override |
| public void initialize(DataCacheManager mgr) { |
| super.initialize(mgr); |
| for(DataCache part : _partitions.values()){ |
| part.initialize(mgr); |
| } |
| } |
| /** |
| * Sets the type of the partitions. |
| * Each partition is a DataCache itself. |
| * |
| * @param type the name of the type that implements {@linkplain DataCache} interface. |
| * Aliases such as <code>"concurrent"</code> is also recognized. |
| * |
| * @throws Exception if the given type is not resolvable to a loadable type. |
| */ |
| public void setPartitionType(String type) throws Exception { |
| Value value = conf.getValue("DataCache"); |
| ClassLoader ctxLoader = AccessController.doPrivileged(J2DoPrivHelper.getContextClassLoaderAction()); |
| ClassLoader loader = conf.getClassResolverInstance().getClassLoader(null, ctxLoader); |
| _type = (Class<? extends DataCache>) AccessController.doPrivileged( |
| J2DoPrivHelper.getForNameAction(value.unalias(type), true, loader)); |
| } |
| |
| /** |
| * Set partitions from a String configuration. |
| * |
| * @param parts a String of the form <code>(p1, p2, p3)</code> where p1, p2 etc. itself are plug-in strings |
| * for individual Data Cache configuration. |
| */ |
| public void setPartitions(String parts) { |
| _partProperties.clear(); |
| parsePartitionProperties(parts); |
| PluginListValue partitions = new PluginListValue("partitions"); |
| String[] types = (String[])Array.newInstance(String.class, _partProperties.size()); |
| Arrays.fill(types, _type.getName()); |
| partitions.setClassNames(types); |
| partitions.setProperties(_partProperties.toArray(new String[_partProperties.size()])); |
| DataCache[] array = (DataCache[])partitions.instantiate(_type, conf); |
| for (DataCache part : array) { |
| if (part.getName() == null) |
| throw new UserException(_loc.get("partition-cache-null-partition", parts)); |
| if (_partitions.containsKey(part.getName())) |
| throw new UserException(_loc.get("partition-cache-duplicate-partition", part.getName(), parts)); |
| if (part.getName().equals(DataCache.NAME_DEFAULT)) |
| throw new UserException(_loc.get("partition-cache-default-partition", part.getName(), parts)); |
| _partitions.put(part.getName(), part); |
| } |
| } |
| |
| /** |
| * Returns the individual partition configuration properties. |
| */ |
| public List<String> getPartitions() { |
| return _partProperties; |
| } |
| |
| @Override |
| public DataCache getPartition(String name, boolean create) { |
| return _partitions.get(name); |
| } |
| |
| /** |
| * Gets the name of the configured partitions. |
| */ |
| @Override |
| public Set<String> getPartitionNames() { |
| return _partitions.keySet(); |
| } |
| |
| /** |
| * Always returns true. |
| */ |
| @Override |
| public final boolean isPartitioned() { |
| return !_partitions.isEmpty(); |
| } |
| |
| @Override |
| public void endConfiguration() { |
| if (!isPartitioned()) |
| conf.getConfigurationLog().warn(_loc.get("partition-cache-no-config")); |
| } |
| |
| /** |
| * Parses property string of the form <code>(p1),(p2),(p3)</code> to produce a list of |
| * <code>p1</code>, <code>p2</code> and <code>p3</code>. The component strings |
| * <code>p1</code> etc. must be enclosed in parentheses and separated by comma. |
| * plug-in string to produce a list of |
| * |
| * @param properties property string of the form <code>(p1),(p2),(p3)</code> |
| */ |
| private void parsePartitionProperties(String full) { |
| String properties = new String(full); |
| while (true) { |
| if (properties == null) |
| break; |
| properties = properties.trim(); |
| if (properties.length() == 0) |
| break; |
| if (properties.startsWith(",")) { |
| properties = properties.substring(1); |
| } else if (!_partProperties.isEmpty()) { |
| throw new UserException(_loc.get("partition-cache-parse-error-comma", full, properties)); |
| } |
| if (properties.startsWith("(") && properties.endsWith(")")) { |
| int i = properties.indexOf(")"); |
| String p = properties.substring(1,i); // exclude the end parentheses |
| _partProperties.add(p); |
| if (i < properties.length()-1) { |
| properties = properties.substring(i+1); |
| } else { |
| break; |
| } |
| } else { |
| throw new UserException(_loc.get("partition-cache-parse-error-paren", full, properties)); |
| } |
| } |
| } |
| } |