/* JAI-Ext - OpenSource Java Advanced Image Extensions Library
* http://www.geo-solutions.it/
* Copyright 2014 - 2015 GeoSolutions
* Licensed 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 it.geosolutions.jaiext;
import it.geosolutions.jaiext.ConcurrentOperationRegistry.OperationCollection;
import it.geosolutions.jaiext.ConcurrentOperationRegistry.OperationItem;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.jai.JAI;
import javax.media.jai.OperationDescriptor;
import javax.media.jai.OperationRegistry;
import javax.media.jai.registry.RenderedRegistryMode;
/**
* Utility class used for registering and unregistering JAI and JAI-EXT operations.
*
* @author Nicola Lagomarsini - GeoSolutions
*
*/
public class JAIExt {
private static final JAI DEFAULT_INSTANCE = JAI.getDefaultInstance();
public static final String OPERATION_CONST_NAME = "operationConst";
public static final String ALGEBRIC_NAME = "algebric";
public static final String STATS_NAME = "Stats";
/** The reader/writer lock for this class. */
private ReadWriteLock lock;
/** The default instance of the {@link JAIExt} class */
private static JAIExt jaiext;
/** {@link OperationRegistry} used by the {@link JAIExt} class */
private ConcurrentOperationRegistry registry;
/** {@link Logger} used for Logging any excpetion or warning */
private static final Logger LOGGER = Logger.getLogger(JAIExt.class.toString());
/** Set containing the mapping from/to jai/jaiext operation names*/
static final HashMap<String, Set<String>> NAME_MAPPING;
static{
// Instantiating the map
NAME_MAPPING = new HashMap<String, Set<String>>();
// Add statistics names
Set<String> stats = new TreeSet<String>();
stats.add("Extrema");
stats.add("Histogram");
stats.add("Mean");
NAME_MAPPING.put(STATS_NAME, stats);
// Add operation Consts names
Set<String> opConst = new TreeSet<String>();
opConst.add("AddConst");
opConst.add("DivideByConst");
opConst.add("MultiplyConst");
opConst.add("SubtractConst");
opConst.add("SubtractFromConst");
opConst.add("AndConst");
opConst.add("OrConst");
opConst.add("XorConst");
NAME_MAPPING.put(OPERATION_CONST_NAME, opConst);
// Add Algebric names
Set<String> algebric = new TreeSet<String>();
algebric.add("Add");
algebric.add("Divide");
algebric.add("Multiply");
algebric.add("Subtract");
algebric.add("Absolute");
algebric.add("And");
algebric.add("Or");
algebric.add("Xor");
algebric.add("Exp");
algebric.add("Log");
algebric.add("Invert");
algebric.add("SubtractFrom");
algebric.add("DivideInto");
NAME_MAPPING.put(ALGEBRIC_NAME, algebric);
}
/**
* {@code true} if JAI media lib is available.
*/
private static final boolean mediaLibAvailable;
static {
// do we wrappers at hand?
boolean mediaLib = false;
Class<?> mediaLibImage = null;
try {
mediaLibImage = Class.forName("com.sun.medialib.mlib.Image");
} catch (ClassNotFoundException e) {
}
mediaLib = (mediaLibImage != null);
// npw check if we either wanted to disable explicitly and if we installed the native libs
if (mediaLib) {
try {
// explicit disable
mediaLib = !Boolean.getBoolean("com.sun.media.jai.disableMediaLib");
// native libs installed
if (mediaLib) {
final Class<?> mImage = mediaLibImage;
mediaLib = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
try {
// get the method
final Class<?> params[] = {};
Method method = mImage.getDeclaredMethod("isAvailable", params);
// invoke
final Object paramsObj[] = {};
final Object o = mImage.newInstance();
return (Boolean) method.invoke(o, paramsObj);
} catch (Throwable e) {
return false;
}
}
});
}
} catch (Throwable e) {
// Because the property com.sun.media.jai.disableMediaLib isn't
// defined as public, the users shouldn't know it. In most of
// the cases, it isn't defined, and thus no access permission
// is granted to it in the policy file. When JAI is utilized in
// a security environment, AccessControlException will be thrown.
// In this case, we suppose that the users would like to use
// medialib accelaration. So, the medialib won't be disabled.
// The fix of 4531501
mediaLib = false;
}
}
mediaLibAvailable = mediaLib;
}
/**
* Initialization of the {@link JAIExt} instance.
* Default behavior is using JAIExt operations
*/
public synchronized static void initJAIEXT() {
initJAIEXT(true);
}
/** Initialization of the {@link JAIExt} instance */
public synchronized static void initJAIEXT(boolean useJaiExtOps) {
initJAIEXT(useJaiExtOps, false);
}
/** Initialization of the {@link JAIExt} instance */
public synchronized static void initJAIEXT(boolean useJaiExtOps, boolean forceReInit) {
if (jaiext == null || forceReInit) {
jaiext = getJAIEXT(useJaiExtOps, forceReInit);
}
}
private synchronized static JAIExt getJAIEXT() {
return getJAIEXT(true, false);
}
private synchronized static JAIExt getJAIEXT(boolean useJaiExtOps, boolean forceReInit) {
if (jaiext == null || forceReInit) {
ConcurrentOperationRegistry initializeRegistry = (ConcurrentOperationRegistry) ConcurrentOperationRegistry.initializeRegistry(useJaiExtOps);
jaiext = new JAIExt(initializeRegistry);
DEFAULT_INSTANCE.setOperationRegistry(initializeRegistry);
}
return jaiext;
}
public static void registerAllOperations(boolean jaiextOperations) {
JAIExt je = getJAIEXT();
if(jaiextOperations){
List<OperationItem> jaiextOps = getJAIEXTOperations();
for(OperationItem item : jaiextOps){
String itemName = item.getName();
String jaiExtName = getOperationName(itemName);
if(itemName != jaiExtName){
OperationItem itemJE = getRegistry().getOperationCollection().get(jaiExtName);
if(itemJE == null){
itemJE = searchForOperation(jaiExtName, true);
je.registerOperation(itemJE);
}
}else{
registerJAIEXTDescriptor(itemName);
}
}
} else{
List<OperationItem> jaiOps = getJAIOperations();
for(OperationItem item : jaiOps){
String itemName = item.getName();
String jaiExtName = getJAIExtName(itemName);
if(itemName != jaiExtName){
OperationItem itemJE = getRegistry().getOperationCollection().get(jaiExtName);
if(itemJE != null){
je.unregisterOperation(itemJE);
}
}else{
registerJAIDescriptor(itemName);
}
}
}
}
private static OperationItem searchForOperation(String jaiExtName, boolean jaiext) {
JAIExt je = getJAIEXT();
List<OperationItem> operations = jaiext ? getJAIEXTOperations() : je.getJAIAvailableOps();
for(OperationItem it : operations){
if(it.getName().equalsIgnoreCase(jaiExtName)){
return it;
}
}
return null;
}
public static void registerOperations(Set<String> operations, boolean jaiext) {
JAIExt je = getJAIEXT();
for (String opName : operations) {
String jaiextName = getJAIExtName(opName);
if (jaiext) {
if (!isJAIExtOperation(jaiextName)) {
registerJAIEXTDescriptor(jaiextName);
}
} else if (jaiextName.equalsIgnoreCase(opName) && isJAIExtOperation(opName) && !NAME_MAPPING.containsKey(jaiextName)) {
registerJAIDescriptor(opName);
} else if(!jaiextName.equalsIgnoreCase(opName) || NAME_MAPPING.containsKey(jaiextName)){
OperationItem it = searchForOperation(jaiextName, true);
je.unregisterOperation(it);
}
}
}
/**
* This method unregister a JAI operation and register the related JAI-EXT one
*
* @param descriptorName
*/
public static void registerJAIEXTDescriptor(String descriptorName) {
getJAIEXT().registerOp(descriptorName, true);
}
/**
* This method unregister a JAI-EXT operation and register the related JAI one
*
* @param descriptorName
*/
public static void registerJAIDescriptor(String descriptorName) {
getJAIEXT().registerOp(descriptorName, false);
}
/**
* This method sets/unsets the netive acceleration for a JAI operation
*
* @param descriptorName
* @param accelerated
*/
public static void setJAIAcceleration(String descriptorName, boolean accelerated) {
getJAIEXT().setAcceleration(descriptorName, accelerated);
}
/**
* Gets a List of all the operations currently registered.
*
* @return
*/
public static List<OperationItem> getOperations() {
return getJAIEXT().getItems();
}
/**
* Get a List of the available JAI-EXT operations
*
* @return
*/
public static List<OperationItem> getJAIEXTOperations() {
return getJAIEXT().getJAIEXTAvailableOps();
}
/**
* Get a List of the available JAI operations
*
* @return
*/
public static List<OperationItem> getJAIOperations() {
return getJAIEXT().getJAIAvailableOps();
}
/**
* Returns the current {@link ConcurrentOperationRegistry} used.
*
* @return
*/
public static ConcurrentOperationRegistry getRegistry() {
return getJAIEXT().registry;
}
/**
* Utility method for substituting the JAI operation names with the JAI-EXT ones, if they are present
*
* @param name
* @return
*/
public static String getOperationName(String name) {
if (isJAIExtOperation(STATS_NAME)
&& NAME_MAPPING.get(STATS_NAME).contains(name)) {
return STATS_NAME;
} else if (isJAIExtOperation(ALGEBRIC_NAME)
&& NAME_MAPPING.get(ALGEBRIC_NAME).contains(name)) {
return ALGEBRIC_NAME;
} else if (isJAIExtOperation(OPERATION_CONST_NAME)
&& NAME_MAPPING.get(OPERATION_CONST_NAME).contains(name)) {
return OPERATION_CONST_NAME;
}
return name;
}
/**
* Utility method for substituting the JAI operation names with the JAI-EXT ones
*
* @param name
* @return
*/
public static String getJAIExtName(String name) {
if (NAME_MAPPING.get(STATS_NAME).contains(name)) {
return STATS_NAME;
} else if (NAME_MAPPING.get(ALGEBRIC_NAME).contains(name)) {
return ALGEBRIC_NAME;
} else if (NAME_MAPPING.get(OPERATION_CONST_NAME).contains(name)) {
return OPERATION_CONST_NAME;
}
return name;
}
/**
* Utility method for substituting the JAIExt operation name with the related JAI one/ones
*
* @param name
* @return
*/
public static List<String> getJAINames(String name) {
List<String> names = new ArrayList<String>();
if(NAME_MAPPING.containsKey(name)){
names.addAll(NAME_MAPPING.get(name));
}else {
names.add(name);
}
return names;
}
/**
* Indicates if the operation is registered as JAI-EXT.
*
* @param descriptorName
* @return
*/
public static boolean isJAIExtOperation(String descriptorName){
return getJAIEXT().isJAIExtOp(descriptorName);
}
/**
* Indicates if the operation is registered as JAI.
*
* @param descriptorName
* @return
*/
public static boolean isJAIAPI(String descriptorName){
return getJAIEXT().isJAIAvailableOperation(descriptorName);
}
private JAIExt(ConcurrentOperationRegistry registry) {
this.registry = registry;
this.lock = new ReentrantReadWriteLock();
}
/**
* Registers the operation defined by the descriptor name. The boolean indicates if the initial operation was a JAI or a JAI-EXT one.
*
* @param descriptorName
* @param fromJAI
*/
private void registerOp(String descriptorName, boolean fromJAI) {
Lock writeLock = lock.writeLock();
try {
writeLock.lock();
// Selection of the input vendor name
String vendor = fromJAI ? (descriptorName.equalsIgnoreCase("Null") ? ConcurrentOperationRegistry.JAI_PRODUCT_NULL
: ConcurrentOperationRegistry.JAI_PRODUCT)
: ConcurrentOperationRegistry.JAIEXT_PRODUCT;
// Check if the old descriptor is present
OperationDescriptor op = (OperationDescriptor) registry.getDescriptor(
RenderedRegistryMode.MODE_NAME, descriptorName);
boolean registered = false;
String inVendor = fromJAI ? "JAI" : "JAI-EXT";
String outVendor = fromJAI ? "JAI-EXT" : "JAI";
// Ensure that the descriptor has the same vendor from that indicated by the vendor, string.
// Otherwise it is already registered or it is not a JAI/JAI-EXT operation.
if (op != null
&& op.getResourceBundle(null)
.getString(ConcurrentOperationRegistry.VENDOR_NAME)
.equalsIgnoreCase(vendor) || op == null) {
// Selection of the OperationItem associated to the old descriptor
OperationCollection collection = registry.getOperationCollection();
OperationItem oldItem = collection.get(descriptorName);
// Getting the JAI/JAI-EXT associated OperationItem
OperationItem newItem = registry.getOperationMap(!fromJAI).get(descriptorName);
// Unregistering of the old operation and registering of the new one
if (newItem != null) {
if(op != null){
if (oldItem != null && oldItem.getCurrentFactory() != null) {
//registry.unregisterFactory(RenderedRegistryMode.MODE_NAME, descriptorName,
//vendor, oldItem.getCurrentFactory());
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Unregistered Factory for the operation: "
+ descriptorName);
}
}
registry.unregisterDescriptor(op);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Unregistered " + inVendor
+ " descriptor for the operation: " + descriptorName);
}
}
registry.registerDescriptor(newItem.getDescriptor());
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Registered " + outVendor
+ " descriptor for the operation: " + descriptorName);
}
Object newFactory = newItem.getCurrentFactory();
if (newFactory != null && registry.getFactory(RenderedRegistryMode.MODE_NAME, newItem.getDescriptor().getName()) == null) {
registry.registerFactory(RenderedRegistryMode.MODE_NAME, descriptorName,
newItem.getVendor(), newFactory);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Registered Factory for the operation: "
+ descriptorName);
}
}
registered = true;
}
}
// Log the operation
if (!registered) {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Unable to register the descriptor related "
+ "to the following operation: " + descriptorName);
}
} else {
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Registered operation: " + descriptorName);
}
}
} finally {
writeLock.unlock();
}
}
/**
* This method sets or unsets the acceleration for the input operation if it is a JAI one.
*
* @param descriptorName
* @param accelerated
*/
private void setAcceleration(String descriptorName, boolean accelerated) {
Lock writeLock = lock.writeLock();
try {
writeLock.lock();
// Selection of the OperationItem associated to the descriptor
OperationCollection collection = registry.getOperationCollection();
OperationItem item = collection.get(descriptorName);
// Selection of the old JAI OperationItem.
OperationItem jaiItem = registry.getOperationMap(true).get(descriptorName);
if (item != null && item.getVendor().equalsIgnoreCase(ConcurrentOperationRegistry.JAI_PRODUCT)
&& jaiItem.getFactory() != null && jaiItem.getMlibFactory() != null) {
// Definition of the old and the new factories
Object oldFactory = null;
Object newFactory = null;
// Acceleration String
String acc1 = null;
String acc2 = null;
// Setting/ Unsetting of the acceleration
if (accelerated) {
oldFactory = jaiItem.getFactory();
newFactory = jaiItem.getMlibFactory();
acc1 = " Accelerated";
acc2 = "";
} else {
newFactory = jaiItem.getFactory();
oldFactory = jaiItem.getMlibFactory();
acc1 = "";
acc2 = " Accelerated";
}
// Registration step
List orderedFactoryList = registry.getOrderedFactoryList(
RenderedRegistryMode.MODE_NAME, descriptorName,
ConcurrentOperationRegistry.JAI_PRODUCT);
if (orderedFactoryList != null && orderedFactoryList.contains(oldFactory)) {
// registry.unregisterFactory(RenderedRegistryMode.MODE_NAME, descriptorName,
// ConcurrentOperationRegistry.JAI_PRODUCT, oldFactory);
registry.unregisterDescriptor(jaiItem.getDescriptor());
registry.registerDescriptor(jaiItem.getDescriptor());
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Unregistered" + acc1
+ " Factory for the operation: " + descriptorName);
}
}
orderedFactoryList = registry.getOrderedFactoryList(RenderedRegistryMode.MODE_NAME,
descriptorName, ConcurrentOperationRegistry.JAI_PRODUCT);
if (orderedFactoryList == null || !orderedFactoryList.contains(newFactory)) {
registry.registerFactory(RenderedRegistryMode.MODE_NAME, descriptorName,
ConcurrentOperationRegistry.JAI_PRODUCT, newFactory);
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "Registered" + acc2
+ " Factory for the operation: " + descriptorName);
}
}
}else{
if (LOGGER.isLoggable(Level.INFO)) {
LOGGER.log(Level.INFO, "Unable to set acceleration for following operation: " + descriptorName);
}
}
} finally {
writeLock.unlock();
}
}
/**
* Returns a List of the operations currently registered
*
* @return
*/
private List<OperationItem> getItems() {
Lock readLock = lock.readLock();
try {
readLock.lock();
Collection<OperationItem> items = registry.getOperations();
List<OperationItem> ops = new ArrayList<OperationItem>(items.size());
ops.addAll(items);
return ops;
} finally {
readLock.unlock();
}
}
/**
* Returns a List of the operations currently registered which are present in the JAI-EXT API:
*
* @return
*/
private List<OperationItem> getJAIEXTAvailableOps() {
Lock readLock = lock.readLock();
try {
readLock.lock();
//OperationCollection items = registry.getOperationCollection();
Map<String, OperationItem> operationMap = registry.getOperationMap(false);
List<OperationItem> ops = new ArrayList<OperationItem>(operationMap.size());
ops.addAll(operationMap.values());
//for (String key : jaiextKeys) {
//ops.add(items.get(key));
//}
return ops;
} finally {
readLock.unlock();
}
}
/**
* Returns a List of the operations currently registered which are present in the JAI-EXT API:
*
* @return
*/
private List<OperationItem> getJAIAvailableOps() {
Lock readLock = lock.readLock();
try {
readLock.lock();
//OperationCollection items = registry.getOperationCollection();
Map<String, OperationItem> operationMap = registry.getOperationMap(true);
List<OperationItem> ops = new ArrayList<OperationItem>(operationMap.size());
ops.addAll(operationMap.values());
//for (String key : jaiKeys) {
//ops.add(items.get(key));
//}
return ops;
} finally {
readLock.unlock();
}
}
private boolean isJAIExtOp(String descriptorName) {
Lock readLock = lock.readLock();
try {
readLock.lock();
OperationCollection items = registry.getOperationCollection();
OperationItem operationItem = items.get(descriptorName);
if(operationItem == null){
return false;
}
return operationItem.getVendor().equalsIgnoreCase(ConcurrentOperationRegistry.JAIEXT_PRODUCT);
} finally {
readLock.unlock();
}
}
private boolean isJAIAvailableOperation(String descriptorName) {
Lock readLock = lock.readLock();
try {
readLock.lock();
String jaiextName = getJAIExtName(descriptorName);
if (!jaiextName.equalsIgnoreCase(descriptorName)) {
return true;
}
List<OperationItem> ops = getJAIAvailableOps();
for (OperationItem item : ops) {
String vendor = item.getVendor();
if ((vendor.equalsIgnoreCase(ConcurrentOperationRegistry.JAI_PRODUCT) || vendor
.equalsIgnoreCase(ConcurrentOperationRegistry.JAI_PRODUCT_NULL))
&& item.getName().equalsIgnoreCase(descriptorName)) {
return true;
}
}
return false;
} finally {
readLock.unlock();
}
}
private void unregisterOperation(OperationItem op) {
Lock writeLock = lock.writeLock();
try {
writeLock.lock();
if (op == null) {
return;
}
//Object factory = op.getFactory();
OperationDescriptor descriptor = op.getDescriptor();
//registry.unregisterFactory(RenderedRegistryMode.MODE_NAME, op.getName(),
//op.getVendor(), factory);
registry.unregisterDescriptor(descriptor);
} finally {
writeLock.unlock();
}
}
private void registerOperation(OperationItem op) {
Lock writeLock = lock.writeLock();
try {
writeLock.lock();
if (op == null) {
return;
}
Object factory = op.getFactory();
OperationDescriptor descriptor = op.getDescriptor();
registry.registerDescriptor(descriptor);
registry.registerFactory(RenderedRegistryMode.MODE_NAME, op.getName(), op.getVendor(),
factory);
} finally {
writeLock.unlock();
}
}
public static boolean isMedialibavailable() {
return mediaLibAvailable;
}
}