alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (GenericCrudCommand.java)

This example Glassfish source code file (GenericCrudCommand.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

annotation, bean, class, componentexception, componentexception, configbeanproxy, javabean, list, list, log, logging, method, object, object, override, reflection, string, string, the, util

The Glassfish GenericCrudCommand.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010 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 org.glassfish.config.support;

import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.hk2.component.InhabitantsFile;
import com.sun.hk2.component.InjectionResolver;
import com.sun.logging.LogDomains;
import org.glassfish.api.Param;
import org.glassfish.api.admin.CommandModelProvider;
import org.glassfish.common.util.admin.ParamTokenizer;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Multiple;
import org.jvnet.hk2.component.ComponentException;
import org.jvnet.hk2.component.Inhabitant;
import org.jvnet.hk2.component.InjectionManager;
import org.jvnet.hk2.component.PostConstruct;
import org.jvnet.hk2.config.*;
import org.jvnet.tiger_types.Types;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * services pertinent to generic CRUD command implementations
 *
 * @author Jerome Dochez
 *
 */
public abstract class GenericCrudCommand implements CommandModelProvider, PostConstruct {
    
    private InjectionResolver<Param> injector;

    @Inject
    Inhabitant<?> myself;    

    final protected static Logger logger = LogDomains.getLogger(GenericCrudCommand.class, LogDomains.ADMIN_LOGGER);
    final protected static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(GenericCrudCommand.class);

    String commandName;
    Class<ConfigBeanProxy> parentType=null;
    Class<ConfigBeanProxy> targetType=null;
    Method targetMethod;
    // default level of noise, useful for just swithching these classes in debugging.
    protected final Level level = Level.FINE;

    public void postConstruct() {
        List<String> indexes = myself.metadata().get(InhabitantsFile.INDEX_KEY);
        if (indexes.size()!=1) {
            StringBuffer sb = new StringBuffer();
            for (String index : indexes) {
                sb.append(index).append(" ");
            }
            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                    "GenericCrudCommand.too_many_indexes",
                    "The metadata for this generic implementation has more than one index {0}",
                    sb.toString());
            Object[] params = new Object[] { sb.toString()};
            logger.log(Level.SEVERE, "GenericCrudCommand.too_many_indexes", params);
            throw new ComponentException(msg);
        }
        String index = indexes.get(0);
        if (index.indexOf(":")==-1) {
            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                    "GenericCrudCommand.unamed_service",
                    "The service {0} is un-named, for generic command, the service name is the command name and must be provided",
                    index);
            Object[] params = new Object[] { index};
            logger.log(Level.SEVERE, "GenericCrudCommand.unamed_service", params);
            throw new ComponentException(msg);            
        }
        commandName = index.substring(index.indexOf(":")+1);
        String parentTypeName = myself.metadata().get(InhabitantsFile.TARGET_TYPE).get(0);
        if (logger.isLoggable(level)) {
            logger.log(level,"Generic method parent targeted type is " + parentTypeName);
        }

        try {
            parentType = (Class<ConfigBeanProxy>) loadClass(parentTypeName);
        } catch(ClassNotFoundException e) {
            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                    "GenericCrudCommand.configbean_not_found",
                    "The Config Bean {0} cannot be loaded by the generic command implementation : {1}",
                    parentTypeName, e.getMessage());
            Object[] params = new Object[] { parentTypeName, e.getMessage()};
            logger.log(Level.SEVERE, "GenericCrudCommand.configbean_not_found",params);
            throw new ComponentException(msg, e);
        }

        // find now the accessor method.
        String methodName = myself.metadata().get("method-name").get(0);
        targetMethod=null;
        for (Method m : parentType.getMethods()) {
            if (m.getName().equals(methodName)) {
                targetMethod=m;
                break;
            }
        }

        if (targetMethod==null) {
            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                    "GenericCrudCommand.configbean_not_found",
                    "The Config Bean {0} cannot be loaded by the generic command implementation : {1}",
                    parentTypeName, methodName);
            Object[] params = new Object[] { parentTypeName, methodName};
            logger.log(Level.SEVERE,"GenericCrudCommand.configbean_not_found", params);
            throw new ComponentException(msg);
        }

        if (targetMethod.getParameterTypes().length==0) {
            // return type matters.
            targetType = Types.erasure(Types.getTypeArgument(
                            targetMethod.getGenericReturnType(),0));
        } else {
            targetType = (Class<ConfigBeanProxy>) targetMethod.getParameterTypes()[0];
        }
    }

    protected <T extends Annotation> T getAnnotation(Method target, Class type) {


        T annotation = targetMethod.getAnnotation(type);
        if (annotation==null) {
            // we need to check for any annotation that has the @Multiple annotation
            for (Annotation a : targetMethod.getAnnotations()) {
                Multiple multiple = a.annotationType().getAnnotation(Multiple.class);
                if (multiple!=null) {
                    try {
                        Method m = a.getClass().getMethod("value");
                        Annotation[] potentials = (Annotation[]) m.invoke(a);
                        if (potentials!=null) {
                            for (Annotation potential : potentials) {
                                if (potential.annotationType().equals(type)) {
                                    m = potential.getClass().getMethod("value");
                                    String value = (String) m.invoke(potential);
                                    if (value.equals(commandName)) {
                                        return type.cast(potential);
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        // ignore
                    }
                }
            }
            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                    "GenericCrudCommand.annotation_not_found",
                    "Cannot find annotation {0} with value {1} on method {2}",
                    type.getName(), commandName, targetMethod.toString());
            throw new RuntimeException(msg);
        }
        return annotation;
    }

    /**
     * we need to have access to the injector instance that has all the parameters context 
     * @param injector the original command injector
     */
    // todo : would be lovely to replace this with some smart injection...
    public void setInjectionResolver(InjectionResolver<Param> injector) {
        this.injector = injector;
    }

    public InjectionResolver<Param> getInjectionResolver() {
        final InjectionResolver<Param> delegate = injector;
        return new InjectionResolver<Param>(Param.class) {
            @Override
            public <V> V getValue(Object component, Inhabitant onBehalfOf, AnnotatedElement annotated, Type genericType, Class type) throws ComponentException {
                if (type.isAssignableFrom(List.class)) {
                    final List<ConfigBeanProxy> values;
                    try {
                        if (annotated instanceof Method) {
                            values = (List<ConfigBeanProxy>) ((Method) annotated).invoke(component);
                        } else if (annotated instanceof Field) {
                            values = (List<ConfigBeanProxy>) ((Field) annotated).get(component);
                        } else {
                            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                    "GenericCrudCommand.invalid_type",
                                    "Invalid annotated type {0} passed to InjectionResolver:getValue()",
                                    annotated.getClass().toString());
                            Object[] params = new Object[] { annotated.getClass().toString()};
                            logger.log(Level.SEVERE, "GenericCrudCommand.invalid_type", params);
                            throw new ComponentException(msg);
                        }
                    } catch (IllegalAccessException e) {
                        String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                "GenericCrudCommand.invocation_failure",
                                "Failure {0} while getting List<?> values from component",
                                e.getMessage());
                        Object[] params = new Object[] { e.getMessage()};
                        logger.log(Level.SEVERE, "GenericCrudCommand.invocation_failure", params);
                        throw new ComponentException(msg, e);
                    } catch (InvocationTargetException e) {
                        String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                "GenericCrudCommand.invocation_failure",
                                "Failure {0} while getting List<?> values from component",
                                e.getMessage());
                        Object[] params = new Object[] { e.getMessage()};
                        logger.log(Level.SEVERE, "GenericCrudCommand.invocation_failure", params);
                        throw new ComponentException(msg, e);
                    }
                    Object value = delegate.getValue(component, null, annotated, genericType, type);
                    if (value==null) {
                        if (logger.isLoggable(level)) {
                            logger.log(level, "Value of " + annotated.toString() + " is null");
                        }
                        return null;
                    }
                    final Class<? extends ConfigBeanProxy> itemType = Types.erasure(Types.getTypeArgument(
                            annotated instanceof Method?
                            ((Method) annotated).getGenericReturnType():((Field) annotated).getGenericType(), 0));
                    if (logger.isLoggable(level)) {
                        logger.log(level, "Found that List<?> really is a List<" + itemType.toString() + ">");
                    }
                    if (itemType==null) {
                            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                    "GenericCrudCommand.nongeneric_type",
                                    "The List type returned by {0} must be a generic type",
                                    annotated.toString());
                            Object[] params = new Object[] {annotated.toString()};
                            logger.log(Level.SEVERE, "GenericCrudCommand.nongeneric_type", params);
                            throw new ComponentException(msg);
                    }
                    if (!ConfigBeanProxy.class.isAssignableFrom(itemType)) {
                        String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                "GenericCrudCommand.wrong_type",
                                "The generic type {0} is not supported, only List<? extends ConfigBeanProxy> is",
                                annotated.toString());
                        Object[] params = new Object[] { annotated.toString()};
                        logger.log(Level.SEVERE, "GenericCrudCommand.wrong_type", params);
                        throw new ComponentException(msg);
                        
                    }
                    Properties props = convertStringToProperties(value.toString(), ':');
                    if (logger.isLoggable(level)) {
                        for (Map.Entry<Object, Object> entry : props.entrySet()) {
                            logger.log(level, "Subtype " + itemType + " key:" + entry.getKey() + " value:" + entry.getValue());
                        }
                    }
                    final BeanInfo beanInfo;
                    try {
                        beanInfo = Introspector.getBeanInfo(itemType);
                    } catch (IntrospectionException e) {
                        String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                "GenericCrudCommand.introspection_failure",
                                "Failure {0} while instrospecting {1} to find all getters and setters",
                                e.getMessage(), itemType.getName());
                        Object[] params = new Object[] { e.getMessage(), itemType.getName()};
                        logger.log(Level.SEVERE, "GenericCrudCommand.introspection_failure", params);
                        throw new ComponentException(msg, e);
                    }
                    for (final Map.Entry<Object, Object> entry : props.entrySet()) {
                        ConfigBeanProxy child = (ConfigBeanProxy) component;
                        try {
                            ConfigBeanProxy cc = child.createChild(itemType);
                            new InjectionManager().inject(cc, itemType, new InjectionResolver<Attribute>(Attribute.class) {

                                @Override
                                public boolean isOptional(AnnotatedElement annotated, Attribute annotation) {
                                    return true;    
                                }

                                @Override
                                public Method getSetterMethod(Method annotated, Attribute annotation) {
                                    // Attribute annotation are always annotated on the getter, we need to find the setter
                                    // variant.
                                    for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                                        if (pd.getReadMethod().equals(annotated)) {
                                            return pd.getWriteMethod();
                                        }
                                    }
                                    return annotated;
                                }

                                @Override
                                public <V> V getValue(Object component, Inhabitant onBehalfOf, AnnotatedElement annotated, Type genericType, Class type) throws ComponentException {
                                    String name = annotated.getAnnotation(Attribute.class).value();
                                    if (name==null || name.length()==0) {

                                        // maybe there is a better way to do this...
                                        name = ((Method) annotated).getName().substring(3);

                                        if (name.equalsIgnoreCase("name") || name.equalsIgnoreCase("key")) {
                                            return type.cast(entry.getKey());
                                        }
                                        if (name.equalsIgnoreCase("value")) {
                                            return type.cast(entry.getValue());
                                        }
                                    }
                                    return null;
                                }
                            });
                            values.add(cc);
                        } catch (TransactionFailure transactionFailure) {
                            String msg = localStrings.getLocalString(GenericCrudCommand.class,
                                "GenericCrudCommand.transactionException",
                                "Transaction exception {0} while injecting {1}",
                                transactionFailure.getMessage(), itemType);
                            Object[] params = new Object[] { transactionFailure.getMessage(), itemType};
                            logger.log(Level.SEVERE, "GenericCrudCommand.transactionException", params);
                            throw new ComponentException(msg, transactionFailure);
                        }

                    }
                    return null;
                }
                return delegate.getValue(component, null, annotated, genericType, type);
            }

            @Override
            public boolean isOptional(AnnotatedElement annotated, Param annotation) {
                return annotation.optional();
            }
        };
        
    }

    protected Class loadClass(String type) throws ClassNotFoundException {
        // by default I use the inhabitant class loader
        return myself.type().getClassLoader().loadClass(type);
    }    

    /**
     * Convert a String with the following format to Properties:
     * name1=value1:name2=value2:name3=value3:...
     * The Properties object contains elements:
     * {name1=value1, name2=value2, name3=value3, ...}
     *
     * @param propsString the String to convert
     * @param sep the separator character
     * @return Properties containing the elements in String
     */
    public static Properties convertStringToProperties(String propsString, char sep) {
        final Properties properties = new Properties();
        if ((propsString != null)&&(!propsString.equals("[]"))) {

            //This is because when there are multiple values in the arraylist
            //they appear like [foo=bar:baz=baz1] so need to remove the braces
            String unbracedString = propsString.substring(propsString.indexOf('[')+1);
            
            ParamTokenizer stoken = new ParamTokenizer(unbracedString, sep);
            while (stoken.hasMoreTokens()) {
                String token = stoken.nextTokenKeepEscapes();
                final ParamTokenizer nameTok = new ParamTokenizer(token, '=');
                String name = null, value = null;
                if (nameTok.hasMoreTokens())
                    name = nameTok.nextToken();
                if (nameTok.hasMoreTokens())
                    value = nameTok.nextToken();
                if (nameTok.hasMoreTokens() || name == null || value == null)
                    throw new IllegalArgumentException("TODO : i18n : Invalid property syntax." + propsString);
                        //strings.getLocalString("InvalidPropertySyntax",
                        //    "Invalid property syntax.", propsString));
                int index = value.indexOf(']');
               
                String unbracedValue =index > 0 ? value.substring(0,index) : value;

                properties.setProperty(name, unbracedValue);
            }
        }
        return properties;
    }

    /**
     * Returns the element name used by the parent to store instances of the child
     *
     * @param document the dom document this configuration element lives in.
     * @param parent type of the parent
     * @param child type of the child
     * @return the element name holding child's instances in the parent
     * @throws ClassNotFoundException when subclasses cannot be loaded
     */
    public static String elementName(DomDocument document, Class<?> parent, Class child)
        throws ClassNotFoundException {

        ConfigModel cm = document.buildModel(parent);
        for (String elementName : cm.getElementNames()) {
            ConfigModel.Property prop = cm.getElement(elementName);
            if (prop instanceof ConfigModel.Node) {
                ConfigModel childCM = ((ConfigModel.Node) prop).getModel();
                String childTypeName = childCM.targetTypeName;
                if (childTypeName.equals(child.getName())) {
                    return elementName;
                }
                // check the inheritance hierarchy
                List<ConfigModel> subChildrenModels = document.getAllModelsImplementing(
                        childCM.classLoaderHolder.get().loadClass(childTypeName));
                if (subChildrenModels!=null) {
                    for (ConfigModel subChildModel : subChildrenModels) {
                        if (subChildModel.targetTypeName.equals(child.getName())) {
                            return elementName;
                        }
                    }
                }

            }
        }
        return null;
    }
 
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish GenericCrudCommand.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.