com.sun.enterprise.connectors.deployment.annotation.handlers.ConfigPropertyHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2012 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.connectors.deployment.annotation.handlers;
import com.sun.enterprise.deployment.annotation.context.RarBundleContext;
import com.sun.enterprise.deployment.annotation.handlers.*;
import com.sun.enterprise.deployment.*;
import com.sun.enterprise.util.LocalStringManagerImpl;
import javax.resource.spi.*;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.IllegalStateException;
import java.util.Locale;
import java.util.Set;
import java.util.List;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.apf.impl.HandlerProcessingResultImpl;
import org.glassfish.apf.impl.AnnotationUtils;
import org.glassfish.apf.*;
import org.jvnet.hk2.annotations.Service;
/**
* @author Jagadish Ramu
*/
@Service
@AnnotationHandlerFor(ConfigProperty.class)
public class ConfigPropertyHandler extends AbstractHandler {
protected final static LocalStringManagerImpl localStrings =
new LocalStringManagerImpl(ConfigPropertyHandler.class);
protected final static Logger logger = AnnotationUtils.getLogger();
private static final String SUCCESS = "success";
public HandlerProcessingResult processAnnotation(AnnotationInfo element) throws AnnotationProcessorException {
AnnotatedElementHandler aeHandler = element.getProcessingContext().getHandler();
ConfigProperty configProperty = (ConfigProperty) element.getAnnotation();
if (aeHandler instanceof RarBundleContext) {
RarBundleContext rbc = (RarBundleContext) aeHandler;
ConnectorDescriptor desc = rbc.getDescriptor();
handleConfigPropertyAnnotation(configProperty, element, desc);
} else {
return getFailureResult(element, "not a rar bundle context", true);
}
return getDefaultProcessedResult();
}
public HandlerProcessingResult handleConfigPropertyAnnotation(
ConfigProperty configProperty, AnnotationInfo element, ConnectorDescriptor desc){
String defaultValue = configProperty.defaultValue();
String[] description = configProperty.description();
boolean ignore = configProperty.ignore();
boolean supportsDynamicUpdates = configProperty.supportsDynamicUpdates();
boolean confidential = configProperty.confidential();
Class type ;
if (element.getElementType().equals(ElementType.METHOD)) {
Method m = (Method) element.getAnnotatedElement();
String result = validateMethod(m, configProperty);
if(!result.equals(SUCCESS)){
return getFailureResult(element, result, true);
}
type = getType(configProperty, m.getParameterTypes()[0]);
//XXX: Siva: For now use the first provided description
String firstDesc = "";
if (description.length > 0) firstDesc = description[0];
ConnectorConfigProperty ep = getConfigProperty(defaultValue, firstDesc, ignore,
supportsDynamicUpdates, confidential, type, m.getName().substring(3));
handleConfigPropertyAnnotation(element, desc, ep, m.getDeclaringClass());
} else if (element.getElementType().equals(ElementType.FIELD)) {
Field f = (Field) element.getAnnotatedElement();
String result = validateField(f, configProperty);
if(!result.equals(SUCCESS)){
return getFailureResult(element, result, true);
}
type = getType(configProperty, f.getType());
if(defaultValue == null || defaultValue.equals("")){
defaultValue = deriveDefaultValueOfField(f);
}
//XXX: Siva: For now use the first provided description
String firstDesc = "";
if (description.length > 0) firstDesc = description[0];
ConnectorConfigProperty ep = getConfigProperty(defaultValue,firstDesc, ignore,
supportsDynamicUpdates, confidential, type, f.getName());
handleConfigPropertyAnnotation(element, desc, ep, f.getDeclaringClass());
}
return getDefaultProcessedResult();
}
private static Class getWrapperClass(String primitive){
if(primitive.equalsIgnoreCase("int")){
return java.lang.Integer.class;
}else if(primitive.equalsIgnoreCase("long")){
return java.lang.Long.class;
}else if(primitive.equalsIgnoreCase("short")){
return java.lang.Short.class;
}else if(primitive.equalsIgnoreCase("char")){
return Character.class;
}else if(primitive.equalsIgnoreCase("byte")){
return java.lang.Byte.class;
}else if(primitive.equalsIgnoreCase("boolean")){
return java.lang.Boolean.class;
}else if(primitive.equalsIgnoreCase("float")){
return java.lang.Float.class;
}else if(primitive.equalsIgnoreCase("double")){
return java.lang.Double.class;
}else{
throw new IllegalArgumentException("Could not determine Wrapper class for primitive type ["+primitive+"]");
}
}
private static String deriveDefaultValueOfField(Field f){
Class declaringClass = f.getDeclaringClass();
String fieldName = f.getName();
String value = null;
try {
Object o = declaringClass.newInstance();
String getterMethod = "get" + getCamelCasedPropertyName(fieldName);
if(Boolean.class.isAssignableFrom(f.getType())){
getterMethod = "is" + getCamelCasedPropertyName(fieldName);
}
Method m = declaringClass.getDeclaredMethod(getterMethod);
m.setAccessible(true);
Object result = m.invoke(o);
if(result != null) {
value = result.toString();
}
} catch (Exception e) {
Object[] args = {fieldName, declaringClass.getName(), e.getMessage()};
String localString = localStrings.getLocalString(
"enterprise.deployment.annotation.handlers.configpropertyfieldreadfailure",
"failed to read the value of field [{0}] on class [{1}], reason : {2}", args);
logger.log(Level.WARNING, localString, e);
}
return value;
}
/**
* Returns camel-cased version of a propertyName. Used to construct
* correct accessor and mutator method names for a give property.
*/
private static String getCamelCasedPropertyName(String propertyName) {
return propertyName.substring(0, 1).toUpperCase(Locale.getDefault()) +
propertyName.substring(1);
}
private static ConnectorConfigProperty getConfigProperty(String defaultValue, String description, boolean ignore,
boolean supportsDynamicUpdates, boolean confidential,
Class type, String propertyName) {
ConnectorConfigProperty ep = new ConnectorConfigProperty();
//use description if specified
if (!description.equals("")) {
ep.setDescription(description);
}
//use default value if specified
if ( defaultValue!= null && !defaultValue.equals("")) {
ep.setValue(defaultValue);
}
ep.setType(type.getName());
ep.setName(propertyName);
if (!ep.isSetIgnoreCalled()) {
ep.setIgnore(ignore);
}
if (!ep.isSetConfidentialCalled()) {
ep.setConfidential(confidential);
}
if (!ep.isSupportsDynamicUpdates()) {
ep.setSupportsDynamicUpdates(supportsDynamicUpdates);
}
return ep;
}
private void handleConfigPropertyAnnotation(AnnotationInfo element, ConnectorDescriptor desc,
ConnectorConfigProperty ep, Class declaringClass) {
if ((ResourceAdapter.class.isAssignableFrom(declaringClass)
&& (!Modifier.isAbstract(declaringClass.getModifiers()))) ||
( declaringClass.getAnnotation(Connector.class) != null)){
if (!processConnector(desc, ep, declaringClass)) {
//need to book-keep the annotation for post-processing
desc.addConfigPropertyAnnotation(declaringClass.getName(), element);
}
} else if (ManagedConnectionFactory.class.isAssignableFrom(declaringClass) &&
(!Modifier.isAbstract(declaringClass.getModifiers()))) {
//@ConnectionDefintion, @ConnectionDefinitions must be of type ManagedConnectionFactory and hence
//the above check is sufficient to take care of JavaBean as well annotation.
processConnectionDefinition(element, desc, ep, declaringClass);
} else if ((ActivationSpec.class.isAssignableFrom(declaringClass) &&
(!Modifier.isAbstract(declaringClass.getModifiers()))) ||
(declaringClass.getAnnotation(Activation.class) != null) ) {
processActivation(element, desc, ep, declaringClass);
} else if (declaringClass.getAnnotation(AdministeredObject.class) != null ||
isAdminObjectJavaBean(declaringClass, desc)){
processAdministeredObject(element, desc, ep, declaringClass);
}
}
private boolean isAdminObjectJavaBean(Class adminObjectClass, ConnectorDescriptor desc) {
boolean isAdminObject = false;
Set adminObjects = desc.getAdminObjects();
Iterator adminObjectsItr = adminObjects.iterator();
while(adminObjectsItr.hasNext()){
AdminObject adminObject = (AdminObject)adminObjectsItr.next();
if(adminObject.getAdminObjectClass().equals(adminObjectClass.getName())){
isAdminObject = true;
break;
}
}
return isAdminObject;
}
private void processAdministeredObject(AnnotationInfo element, ConnectorDescriptor desc,
ConnectorConfigProperty ep, Class declaringClass) {
if (declaringClass.getAnnotation(AdministeredObject.class) != null) {
AdministeredObject ao = (AdministeredObject) declaringClass.getAnnotation(AdministeredObject.class);
Class[] adminObjectInterfaces = ao.adminObjectInterfaces();
if(adminObjectInterfaces.length > 0){
for (Class adminObjectInterface : adminObjectInterfaces) {
handleAdministeredObject(element, desc, ep, declaringClass, adminObjectInterface);
}
}else{
//handle the case where admin object interfaces are not specified via annotaiton
List interfacesList = AdministeredObjectHandler.
deriveAdminObjectInterfacesFromHierarchy(declaringClass);
//We assume that there will be only one interface (if there had been many, admin-object annotation
//handler would have rejected it.)
if(interfacesList.size() == 1){
Class intf = interfacesList.get(0);
handleAdministeredObject(element, desc, ep, declaringClass, intf);
}
}
} else {
Set adminObjects = desc.getAdminObjects();
Iterator adminObjectItr = adminObjects.iterator();
while(adminObjectItr.hasNext()){
AdminObject adminObject = (AdminObject)adminObjectItr.next();
if(adminObject.getAdminObjectClass().equals(declaringClass.getName())){
if (!(isConfigDefined(adminObject.getConfigProperties(), ep))) {
adminObject.addConfigProperty(ep);
}
String uniqueName = adminObject.getAdminObjectInterface() + "_" + adminObject.getAdminObjectClass();
if(!desc.getConfigPropertyProcessedClasses().contains(uniqueName)){
processParent(declaringClass.getSuperclass(), adminObject.getConfigProperties());
desc.addConfigPropertyProcessedClass(declaringClass.getName());
}
}
}
}
}
private void handleAdministeredObject(AnnotationInfo element, ConnectorDescriptor desc,
ConnectorConfigProperty ep, Class adminObjectClass, Class adminObjectIntf) {
AdminObject adminObject = desc.getAdminObject(adminObjectIntf.getName(), adminObjectClass.getName());
if (adminObject != null) {
if (!(isConfigDefined(adminObject.getConfigProperties(), ep))) {
adminObject.addConfigProperty(ep);
}
if(!desc.getConfigPropertyProcessedClasses().contains(adminObjectClass.getName())){
processParent(adminObjectClass.getSuperclass(), adminObject.getConfigProperties());
desc.addConfigPropertyProcessedClass(adminObjectClass.getName());
}
} else {
// ideally adminObject should not be null as "@AdministeredObject"
// should have been handled before @ConfigProperty
getFailureResult(element, "could not get adminobject of interface " +
"[ " + adminObjectIntf.getName() + " ]" +
" and class [ " + adminObjectClass.getName() + " ]", true);
}
}
private void processActivation(AnnotationInfo element, ConnectorDescriptor desc,
ConnectorConfigProperty ep, Class declaringClass) {
InboundResourceAdapter ira = desc.getInboundResourceAdapter();
if (declaringClass.getAnnotation(Activation.class) != null) {
// Inbound Resource Adapter should have been defined if @Activation annotation
// was processed successfully, before.
if (desc.getInBoundDefined()) {
Activation activation = (Activation) declaringClass.getAnnotation(Activation.class);
Class[] messageListeners = activation.messageListeners();
//messageListeners cant be 0 as we ask "@Activation" to be handled before "@ConfigProperty"
for (Class clz : messageListeners) {
if (ira.hasMessageListenerType(clz.getName())) {
MessageListener ml = ira.getMessageListener(clz.getName());
//check whether the activation-spec class in the descriptor
//for a particular message-listener is the same as this class as it is possible
//that this activation-spec class may have been ignored if ra.xml is already defined with
//this particular message-listener-type. If so, we should not add config-property as they
// belong to a particular activation-spec class.
if (ml.getActivationSpecClass().equals(declaringClass.getName())) {
if (!(isConfigDefined(ml.getConfigProperties(), ep))) {
ml.addConfigProperty(ep);
}
if (!desc.getConfigPropertyProcessedClasses().contains(declaringClass.getName())) {
processParent(declaringClass.getSuperclass(), ml.getConfigProperties());
desc.addConfigPropertyProcessedClass(declaringClass.getName());
}
}
}
}
}
} else {
if(desc.getInBoundDefined()){
Set messageListeners = desc.getInboundResourceAdapter().getMessageListeners();
Iterator mlItr = messageListeners.iterator();
while(mlItr.hasNext()){
MessageListener ml = (MessageListener)mlItr.next();
if(ml.getActivationSpecClass().equals(declaringClass.getName())){
if (!(isConfigDefined(ml.getConfigProperties(), ep))) {
ml.addConfigProperty(ep);
}
if(!desc.getConfigPropertyProcessedClasses().contains(declaringClass.getName())){
processParent(declaringClass.getSuperclass(), ml.getConfigProperties());
desc.addConfigPropertyProcessedClass(declaringClass.getName());
}
}
}
}
}
}
private void processConnectionDefinition(AnnotationInfo element, ConnectorDescriptor desc,
ConnectorConfigProperty ep, Class declaringClass) {
if (desc.getOutBoundDefined()) {
OutboundResourceAdapter ora = desc.getOutboundResourceAdapter();
Set connectionDefinitions = ora.getConnectionDefs();
for (Object o : connectionDefinitions) {
ConnectionDefDescriptor cd = (ConnectionDefDescriptor) o;
if (cd.getManagedConnectionFactoryImpl().equals(declaringClass.getName())) {
if (!(isConfigDefined(cd.getConfigProperties(), ep))) {
cd.addConfigProperty(ep);
}
//As same MCF class can be used for multiple connection-definitions
//store it based on connection-factory-interface class which is the unique
//identifier for a connection-definition
if(!desc.getConfigPropertyProcessedClasses().contains(cd.getConnectionFactoryIntf())){
processParent(declaringClass.getSuperclass(), cd.getConfigProperties());
desc.addConfigPropertyProcessedClass(cd.getConnectionFactoryIntf());
}
}
//ignore if connection-definition entry is not found as it is possible that
//ra.xml has a connection-definition with the same connection-factory class
//as this annotation.
//it is possible that multiple ConnectionDefinitions with same MCF class, but different
//connection-factory-interface can be defined.Hence process all connection definitions
}
} else {
// if there is a @ConfigProperty annotation on any of the connection-definition (MCF), either it is
// defined via ra.xml and hence actual @ConnectionDefinition(s) annotation is ignored
// or
// no clash between ra.xml and the annotation, actual annotation is considered
// So, outbound-ra must have been defined either way.
getFailureResult(element, "Outbound RA is not defined", true);
}
}
public static boolean processConnector(ConnectorDescriptor desc, ConnectorConfigProperty ep, Class declaringClass) {
// make sure that the RA Class considered here is the one specified in descriptor
// If not, it will be processed once the @Connector is selected during post-processing
// handle the annotation specified on a ResourceAdapter JavaBean
// make sure that the property is not already specified in DD
if(desc.getResourceAdapterClass().equals(declaringClass.getName())){
if (!(isConfigDefined(desc.getConfigProperties(), ep))) {
desc.addConfigProperty(ep);
}
if(!desc.getConfigPropertyProcessedClasses().contains(declaringClass.getName())){
processParent(declaringClass.getSuperclass(), desc.getConfigProperties());
desc.addConfigPropertyProcessedClass(declaringClass.getName());
}
//indicate that the config-property is processed
return true;
}else{
//indicate that the config-property is not processed and need to be processed during post-processing
return false;
}
}
private static String validateMethod(Method m, ConfigProperty property){
if(!m.getName().startsWith("set")){
return "not a standard JavaBean setter method : [" + m.getName() + " ] ";
}
int modifier = m.getModifiers();
//we are not restricting protected, default methods as potentially
//any of the sub-classes may broaden the accessibility.
if(Modifier.isPrivate(modifier)){
return "@ConfigProperty annotation on a " +
"private setter method [ "+m.getName()+" ] " +
"of class [ "+m.getDeclaringClass().getName()+" ]";
}
Class type = property.type();
Class[] parameters = m.getParameterTypes();
Class propertyType;
if (parameters.length != 0) {
if (parameters.length == 1) {
propertyType = parameters[0];
} else {
return "more than one parameter for JavaBean setter method : [" + m.getName() + " ] ";
}
} else {
return "no parameters for JavaBean setter method : [" + m.getName() + " ] ";
}
//check compatibility between annotation type and property-type
if (!type.equals(Object.class) && !propertyType.isAssignableFrom(type)) {
if(type.isPrimitive()){
type = getWrapperClass(type.getName());
}else if(propertyType.isPrimitive()){
propertyType = getWrapperClass(propertyType.getName());
}
if(!propertyType.isAssignableFrom(type)){
return "annotation type [" + type + "] and property-type" +
" [" + propertyType + "] " +
"are not assignment compatible";
}
}
return SUCCESS;
}
private static Class getType( ConfigProperty property, Class type){
Class configPropertyType = property.type();
if (configPropertyType.equals(Object.class)) {
configPropertyType = type;
}
return configPropertyType;
}
private static String validateField(Field f, ConfigProperty property){
Class c = f.getDeclaringClass();
Class returnType = f.getType();
Class type = property.type();
if (!type.equals(Object.class)) {
//check compatibility between annotation type and return-type
if (!returnType.isAssignableFrom(type)) {
return "annotation type [" + type + "] " +
"and return-type [" + returnType + "] " +
"are not assignment compatible for @ConfigProperty in " +
"field [ " + f.getName() + " ] of class [ " + c + " ]";
}
}
return SUCCESS;
}
public static void processParent(Class claz, Set configProperties) {
if(claz == null){
return;
}
//process the methods
Method[] methods = claz.getDeclaredMethods();
for (Method m : methods) {
ConfigProperty property = m.getAnnotation(ConfigProperty.class);
if (property != null) {
String result = validateMethod(m, property);
if (!result.equals(SUCCESS)) {
throw new IllegalStateException(result);
}
String defaultValue = property.defaultValue();
Class type = getType(property, m.getParameterTypes()[0]);
processConfigProperty(configProperties, m.getName().substring(3), property, defaultValue, type);
}
}
//process the fields
Field[] fields = claz.getDeclaredFields();
for (Field f : fields) {
ConfigProperty property = f.getAnnotation(ConfigProperty.class);
if (property != null) {
String status = validateField(f, property);
if(!status.equals(SUCCESS)){
throw new IllegalStateException(status);
}
String defaultValue = property.defaultValue();
if (defaultValue == null || defaultValue.equals("")) {
defaultValue = deriveDefaultValueOfField(f);
}
processConfigProperty(configProperties, f.getName(), property, defaultValue, f.getType());
}
}
//process its super-class
if (claz.getSuperclass() != null) {
processParent(claz.getSuperclass(), configProperties);
}
}
private static void processConfigProperty(Set configProperties, String propertyName,
ConfigProperty property, String defaultValue, Class declaredEntityType) {
String description = "";
if(property.description() != null && property.description().length > 0){
description = property.description()[0];
}
Class type = getType(property, declaredEntityType);
ConnectorConfigProperty ccp = getConfigProperty(defaultValue, description,
property.ignore(), property.supportsDynamicUpdates(), property.confidential(),
type, propertyName);
if(!isConfigDefined(configProperties, ccp)){
configProperties.add(ccp);
}
}
private static boolean isConfigDefined(Set configProperties, ConnectorConfigProperty ep) {
boolean result = false;
for (Object o : configProperties) {
ConnectorConfigProperty ddEnvProperty = (ConnectorConfigProperty) o;
if (ddEnvProperty.getName().equals(ep.getName())) {
result = true;
break;
}
}
return result;
}
/**
* @return a default processed result
*/
protected HandlerProcessingResult getDefaultProcessedResult() {
return HandlerProcessingResultImpl.getDefaultResult(
getAnnotationType(), ResultType.PROCESSED);
}
public Class[] getTypeDependencies() {
return new Class[]{Connector.class, ConnectionDefinition.class, ConnectionDefinitions.class,
Activation.class, AdministeredObject.class};
}
private void debug(String s) {
logger.log(Level.INFO, "[ConfigPropertyHandler] " + s);
}
private HandlerProcessingResultImpl getFailureResult(AnnotationInfo element, String message, boolean doLog) {
HandlerProcessingResultImpl result = new HandlerProcessingResultImpl();
result.addResult(getAnnotationType(), ResultType.FAILED);
if (doLog) {
Object o = element.getAnnotatedElement();
String className = null;
if(o instanceof Field){
className = ((Field)o).getDeclaringClass().getName();
}else { //else it can be only METHOD
className = ((Method)o).getDeclaringClass().getName();
}
Object args[] = new Object[]{
element.getAnnotation(),
className,
message,
};
String localString = localStrings.getLocalString(
"enterprise.deployment.annotation.handlers.connectorannotationfailure",
"failed to handle annotation [ {0} ] on class [ {1} ], reason : {2}", args);
logger.log(Level.WARNING, localString);
}
return result;
}
}