org.glassfish.common.util.admin.MapInjectionResolver.java Source code

Java tutorial

Introduction

Here is the source code for org.glassfish.common.util.admin.MapInjectionResolver.java

Source

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 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.common.util.admin;

import com.sun.enterprise.util.LocalStringManagerImpl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.Validator;

import org.apache.commons.net.ftp.FTPClient;
import org.glassfish.api.ExecutionContext;
import org.glassfish.api.Param;
import org.glassfish.api.ParamDefaultCalculator;
import org.glassfish.api.admin.CommandModel;
import org.glassfish.api.admin.ParameterMap;
import org.glassfish.hk2.api.MultiException;
import org.jvnet.hk2.component.MultiMap;
import org.jvnet.hk2.config.InjectionResolver;

/**
 * An InjectionResolver that uses a ParameterMap object as the source of
 * the data to inject.
 */
public class MapInjectionResolver extends InjectionResolver<Param> {
    private final CommandModel model;
    private final ParameterMap parameters;
    private ExecutionContext context = null;

    private final MultiMap<String, File> optionNameToUploadedFileMap;

    public static final LocalStringManagerImpl localStrings = new LocalStringManagerImpl(
            MapInjectionResolver.class);
    private static Validator beanValidator = null;

    public MapInjectionResolver(CommandModel model, ParameterMap parameters) {
        this(model, parameters, null);
    }

    public MapInjectionResolver(CommandModel model, ParameterMap parameters,
            final MultiMap<String, File> optionNameToUploadedFileMap) {
        super(Param.class);
        this.model = model;
        this.parameters = parameters;
        this.optionNameToUploadedFileMap = optionNameToUploadedFileMap;
    }

    /**
     * Set the context that is passed to the DynamicParamImpl.defaultValue method.
     */
    public void setContext(ExecutionContext context) {
        this.context = context;
    }

    @Override
    public boolean isOptional(AnnotatedElement element, Param annotation) {
        String name = CommandModel.getParamName(annotation, element);
        CommandModel.ParamModel param = model.getModelFor(name);
        return param.getParam().optional();
    }

    private void FormatUriToFile() throws IOException {
        List<String> value = parameters.get("DEFAULT");
        String uri = value.get(0);
        URL url = new URL(uri);
        File file = null;

        if (uri.startsWith("file:/")) {
            file = new File(url.getFile());
        } else if (uri.startsWith("http://")) {
            InputStream inStream = url.openStream();
            BufferedInputStream bufIn = new BufferedInputStream(inStream);

            file = new File(System.getenv().get("TEMP") + uri.substring(uri.lastIndexOf("/")));
            if (file.exists()) {
                file.delete();
            }
            OutputStream out = new FileOutputStream(file);
            BufferedOutputStream bufOut = new BufferedOutputStream(out);
            byte buffer[] = new byte[204800];
            while (true) {
                int nRead = bufIn.read(buffer, 0, buffer.length);
                if (nRead <= 0)
                    break;
                bufOut.write(buffer, 0, nRead);
            }
            bufOut.flush();
            out.close();
            inStream.close();
        } else if (uri.startsWith("ftp://")) {
            String pattern = "^ftp://(.+?)(:.+?)?@(\\S+):(\\d+)(\\S+)$";
            Pattern p = Pattern.compile(pattern);
            Matcher m = p.matcher(uri);
            if (m.matches()) {
                String username = m.group(1);
                String password = "";
                if (m.group(2) != null) {
                    password = m.group(2).replace(":", "");
                }
                String ipAddress = m.group(3);
                String port = m.group(4);
                String path = m.group(5);
                FTPClient ftp = new FTPClient();
                ftp.connect(ipAddress);
                ftp.setDefaultPort(Integer.parseInt(port));
                boolean isLogin = ftp.login(username, password);
                if (isLogin) {
                    ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
                    byte[] buf = new byte[204800];
                    int bufsize = 0;

                    file = new File(System.getenv().get("TEMP") + uri.substring(uri.lastIndexOf("/")));
                    if (file.exists()) {
                        file.delete();
                    }
                    OutputStream ftpOut = new FileOutputStream(file);
                    InputStream ftpIn = ftp.retrieveFileStream(path);
                    System.out.println(ftpIn);
                    while ((bufsize = ftpIn.read(buf, 0, buf.length)) != -1) {
                        ftpOut.write(buf, 0, bufsize);
                    }
                    ftpOut.flush();
                    ftpOut.close();
                    ftpIn.close();
                } else {
                    ftp.logout();
                    ftp.disconnect();
                }
            } else {
                localStrings.getLocalString("IncorrectFtpAddress",
                        "The ftp address is not correct, please change another one.");
            }
        }
        if (file != null)
            parameters.set("DEFAULT", file.getAbsolutePath());
    }

    @Override
    public <V> V getValue(Object component, AnnotatedElement target, Type genericType, Class<V> type)
            throws MultiException {
        // look for the name in the list of parameters passed.
        Param param = target.getAnnotation(Param.class);
        String paramName = CommandModel.getParamName(param, target);
        if (param.primary()) {
            // this is the primary parameter for the command
            List<String> value = parameters.get("DEFAULT");
            if ("uri".endsWith(paramName)) {
                try {
                    FormatUriToFile();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                value = parameters.get("DEFAULT");
            }
            if (value != null && value.size() > 0) {
                /*
                 * If the operands are uploaded files, replace the
                 * client-provided values with the paths to the uploaded files.
                 * XXX - assume the lists are in the same order.
                 */
                final List<String> filePaths = getUploadedFileParamValues("DEFAULT", type,
                        optionNameToUploadedFileMap);
                if (filePaths != null) {
                    value = filePaths;
                    // replace the file name operands with the uploaded files
                    parameters.set("DEFAULT", value);
                } else {
                    for (String s : value) {
                        checkAgainstAcceptableValues(target, s);
                    }

                }
                // let's also copy this value to the cmd with a real name
                parameters.set(paramName, value);
                V paramValue = (V) convertListToObject(target, type, value);
                return paramValue;
            }
        }
        if (param.multiple()) {
            List<String> value = parameters.get(paramName);
            if (value != null && value.size() > 0) {
                final List<String> filePaths = getUploadedFileParamValues(paramName, type,
                        optionNameToUploadedFileMap);
                if (filePaths != null) {
                    value = filePaths;
                    // replace the file name operands with the uploaded files
                    parameters.set(paramName, value);
                } else {
                    for (String s : value) {
                        checkAgainstAcceptableValues(target, s);
                    }
                }
            }
            parameters.set(paramName, value);
            V paramValue = (V) convertListToObject(target, type, value);
            return paramValue;
        }
        String paramValueStr = getParamValueString(parameters, param, target, context);

        /*
         * If the parameter is an uploaded file, replace the client-provided
         * value with the path to the uploaded file.
         */
        final String fileParamValueStr = getUploadedFileParamValue(paramName, type, optionNameToUploadedFileMap);
        if (fileParamValueStr != null) {
            paramValueStr = fileParamValueStr;
            parameters.set(paramName, paramValueStr);
        }
        checkAgainstAcceptableValues(target, paramValueStr);

        return paramValueStr != null ? (V) convertStringToObject(target, type, paramValueStr)
                : (V) getParamField(component, target);
    }

    /**
     * Returns the path to the uploaded file if the specified field is of type
     * File and if the field name is the same as one of the option names that
     * was associated with an uploaded File in the incoming command request.
     *
     * @param fieldName name of the field being injected
     * @param fieldType type of the field being injected
     * @param optionNameToFileMap map of field names to uploaded Files
     * @return absolute path of the uploaded file for the field;
     *          null if no file uploaded for this field
     */
    public static String getUploadedFileParamValue(final String fieldName, final Class fieldType,
            final MultiMap<String, File> optionNameToFileMap) {
        if (optionNameToFileMap == null) {
            return null;
        }
        final File uploadedFile = optionNameToFileMap.getOne(fieldName);
        if (uploadedFile != null && fieldType.isAssignableFrom(File.class)) {
            return uploadedFile.getAbsolutePath();
        } else {
            return null;
        }
    }

    /**
     * Returns the paths to the uploaded files if the specified field is of type
     * File[] and if the field name is the same as one of the option names that
     * was associated with an uploaded File in the incoming command request.
     *
     * @param fieldName name of the field being injected
     * @param fieldType type of the field being injected
     * @param optionNameToFileMap map of field names to uploaded Files
     * @return List of absolute paths of the uploaded files for the field;
     *          null if no file uploaded for this field
     */
    public static List<String> getUploadedFileParamValues(final String fieldName, final Class fieldType,
            final MultiMap<String, File> optionNameToFileMap) {
        if (optionNameToFileMap == null) {
            return null;
        }
        final List<File> uploadedFiles = optionNameToFileMap.get(fieldName);
        if (uploadedFiles != null && uploadedFiles.size() > 0
                && (fieldType.isAssignableFrom(File.class) || fieldType.isAssignableFrom(File[].class))) {
            final List<String> paths = new ArrayList(uploadedFiles.size());
            for (File f : uploadedFiles)
                paths.add(f.getAbsolutePath());
            return paths;
        } else {
            return null;
        }
    }

    /**
     * Get the param value.  Checks if the param (option) value
     * is defined on the command line (URL passed by the client)
     * by calling getParameterValue method.  If not, then check
     * for the shortName.  If param value is not given by the
     * shortName (short option) then if the default value is
     * defined return it.
     *
     * @param parameters parameters from the command line.
     * @param param from the annotated Param
     * @param target annotated element
     * @return param value
     */
    // package-private, for testing
    static String getParamValueString(final ParameterMap parameters, final Param param,
            final AnnotatedElement target, final ExecutionContext context) {
        String paramValueStr = getParameterValue(parameters, CommandModel.getParamName(param, target), true);
        if (paramValueStr == null && param.alias().length() > 0) {
            // check for alias
            paramValueStr = getParameterValue(parameters, param.alias(), true);
        }
        if (paramValueStr == null) {
            // check for shortName
            paramValueStr = parameters.getOne(param.shortName());
        }

        // if paramValueStr is still null, then check to
        // see if the defaultValue is defined
        if (paramValueStr == null) {
            final String defaultValue = param.defaultValue();
            paramValueStr = defaultValue.equals("") ? null : defaultValue;
        }
        // if paramValueStr is still null, then check to see if a defaultCalculator
        // is defined, and if so, call the class to get the default value
        if (paramValueStr == null) {
            Class<? extends ParamDefaultCalculator> dc = param.defaultCalculator();
            if (dc != ParamDefaultCalculator.class) {
                try {
                    ParamDefaultCalculator pdc = dc.newInstance();
                    paramValueStr = pdc.defaultValue(context);
                } catch (InstantiationException ex) { // @todo Java SE 7 - use multi catch
                    Logger.getLogger(MapInjectionResolver.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(MapInjectionResolver.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return paramValueStr;
    }

    /**
     * Get the value of the field.  This value is defined in the
     * annotated Param declaration.  For example:
     * <code>
     * @Param(optional=true)
     * String name="server"
     * </code>
     * The Field, name's value, "server" is returned.
     *
     * @param component command class object
     * @param annotated annotated element
     * @return the annotated Field value
     */
    // package-private, for testing
    static Object getParamField(final Object component, final AnnotatedElement annotated) {
        try {
            if (annotated instanceof Field) {
                final Field field = (Field) annotated;
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        field.setAccessible(true);
                        return null;
                    }
                });
                return ((Field) annotated).get(component);
            }
        } catch (Exception e) {
            // unable to get the field value, may not be defined
            // return null instead.
            return null;
        }
        return null;
    }

    /**
     * Searches for the parameter with the specified key in this parameter map.
     * The method returns null if the parameter is not found.
     *
     * @param params the parameter map to search in
     * @param key the property key
     * @param ignoreCase true to search the key ignoring case,
     *                   false otherwise
     * @return the value in this parameter map with the specified key value
     */
    // package-private, for testing
    static String getParameterValue(final ParameterMap params, String key, final boolean ignoreCase) {
        if (ignoreCase) {
            for (Map.Entry<String, List<String>> entry : params.entrySet()) {
                final String paramName = entry.getKey();
                if (paramName.equalsIgnoreCase(key)) {
                    key = paramName;
                    break;
                }
            }
        }
        return params.getOne(key);
    }

    /**
     * Convert the String parameter to the specified type.
     * For example if type is Properties and the String
     * value is: name1=value1:name2=value2:...
     * then this api will convert the String to a Properties
     * class with the values {name1=name2, name2=value2, ...}
     *
     * @param target the target field
     * @param type the type of class to convert
     * @param paramValStr the String value to convert
     * @return Object
     */
    // package-private, for testing
    static Object convertStringToObject(AnnotatedElement target, Class type, String paramValStr) {
        Param param = target.getAnnotation(Param.class);
        Object paramValue = paramValStr;
        if (type.isAssignableFrom(String.class)) {
            paramValue = paramValStr;
        } else if (type.isAssignableFrom(Properties.class)) {
            paramValue = convertStringToProperties(paramValStr, param.separator());
        } else if (type.isAssignableFrom(List.class)) {
            paramValue = convertStringToList(paramValStr, param.separator());
        } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
            String paramName = CommandModel.getParamName(param, target);
            paramValue = convertStringToBoolean(paramName, paramValStr);
        } else if (type.isAssignableFrom(Integer.class) || type.isAssignableFrom(int.class)) {
            String paramName = CommandModel.getParamName(param, target);
            paramValue = convertStringToInteger(paramName, paramValStr);
        } else if (type.isAssignableFrom(String[].class)) {
            paramValue = convertStringToStringArray(paramValStr, param.separator());
        } else if (type.isAssignableFrom(File.class)) {
            return new File(paramValStr);
        }
        return paramValue;
    }

    /**
     * Convert the List<String> parameter to the specified type.
     *
     * @param target the target field
     * @param type the type of class to convert
     * @param paramValList the List of String values to convert
     * @return Object
     */
    // package-private, for testing
    public static Object convertListToObject(AnnotatedElement target, Class type, List<String> paramValList) {
        Param param = target.getAnnotation(Param.class);
        // does this parameter type allow multiple values?
        if (!param.multiple()) {
            if (paramValList.size() == 1)
                return convertStringToObject(target, type, paramValList.get(0));
            throw new UnacceptableValueException(localStrings.getLocalString("TooManyValues",
                    "Invalid parameter: {0}.  This parameter may not have " + "more than one value.",
                    CommandModel.getParamName(param, target)));
        }

        Object paramValue = paramValList;
        if (type.isAssignableFrom(List.class)) {
            // the default case, nothing to do
        } else if (type.isAssignableFrom(String[].class)) {
            paramValue = paramValList.toArray(new String[paramValList.size()]);
        } else if (type.isAssignableFrom(File[].class)) {
            paramValue = convertListToFiles(paramValList);
        } else if (type.isAssignableFrom(Properties.class)) {
            paramValue = convertListToProperties(paramValList);
        }
        // XXX - could handle arrays of other types
        return paramValue;
    }

    /**
     * Convert a String to a Boolean.
     * null --> true
     * "" --> true
     * case insensitive "true" --> true
     * case insensitive "false" --> false
     * anything else --> throw Exception
     *
     * @param paramName the name of the param
     * @param s the String to convert
     * @return Boolean
     */
    private static Boolean convertStringToBoolean(String paramName, String s) {
        if (!ok(s))
            return Boolean.TRUE;

        if (s.equalsIgnoreCase(Boolean.TRUE.toString()))
            return Boolean.TRUE;

        if (s.equalsIgnoreCase(Boolean.FALSE.toString()))
            return Boolean.FALSE;

        String msg = localStrings
                .getLocalString("UnacceptableBooleanValue",
                        "Invalid parameter: {0}.  This boolean option must be set "
                                + "(case insensitive) to true or false.  " + "Its value was set to {1}",
                        paramName, s);

        throw new UnacceptableValueException(msg);
    }

    /**
     * Convert a String to an Integer.
     *
     * @param paramName the name of the param
     * @param s the String to convert
     * @return Integer
     */
    private static Integer convertStringToInteger(String paramName, String s) {
        try {
            return new Integer(s);
        } catch (Exception ex) {
            String msg = localStrings.getLocalString("UnacceptableIntegerValue",
                    "Invalid parameter: {0}.  This integer option must be set "
                            + "to a valid integer.\nIts value was set to {1}",
                    paramName, s);
            throw new UnacceptableValueException(msg);
        }
    }

    /**
     * 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, ...}
     * 
     * Whitespace around names is ignored.
     *
     * @param propsString the String to convert
     * @param sep the separator character
     * @return Properties containing the elements in String
     */
    // package-private, for testing
    static Properties convertStringToProperties(String propsString, char sep) {
        final Properties properties = new Properties();
        if (propsString != null) {
            ParamTokenizer stoken = new ParamTokenizer(propsString, 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().trim();
                if (nameTok.hasMoreTokens())
                    value = nameTok.nextToken();
                if (name == null) // probably "::"
                    throw new IllegalArgumentException(localStrings.getLocalString("PropertyMissingName",
                            "Invalid property syntax, missing property name", propsString));
                if (value == null)
                    throw new IllegalArgumentException(localStrings.getLocalString("PropertyMissingValue",
                            "Invalid property syntax, missing property value", token));
                if (nameTok.hasMoreTokens())
                    throw new IllegalArgumentException(localStrings.getLocalString("PropertyExtraEquals",
                            "Invalid property syntax, \"=\" in value", token));
                properties.setProperty(name, value);
            }
        }
        return properties;
    }

    /**
     * Convert a List of Strings to an array of File objects.
     *
     * @param filesList the List of Strings to convert
     * @return File[] containing the elements in the list
     */
    private static File[] convertListToFiles(List<String> filesList) {
        final File[] files = new File[filesList.size()];
        for (int i = 0; i < filesList.size(); i++)
            files[i] = new File(filesList.get(i));
        return files;
    }

    /**
     * Convert a List of Strings, each with the following format, to Properties:
     * name1=value1
     *
     * @param propsList the List of Strings to convert
     * @return Properties containing the elements in the list
     */
    private static Properties convertListToProperties(List<String> propsList) {
        final Properties properties = new Properties();
        if (propsList != null) {
            for (String prop : propsList) {
                final ParamTokenizer nameTok = new ParamTokenizer(prop, '=');
                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(
                            localStrings.getLocalString("InvalidPropertySyntax", "Invalid property syntax.", prop));
                properties.setProperty(name, value);
            }
        }
        return properties;
    }

    /**
     * Convert a String with the following format to List<String>:
     * string1:string2:string3:...
     * The List object contains elements: string1, string2, string3, ...
     *
     * @param listString - the String to convert
     * @param sep the separator character
     * @return List containing the elements in String
     */
    // package-private, for testing
    static List<String> convertStringToList(String listString, char sep) {
        List<String> list = new java.util.ArrayList();
        if (listString != null) {
            final ParamTokenizer ptoken = new ParamTokenizer(listString, sep);
            while (ptoken.hasMoreTokens()) {
                String token = ptoken.nextToken();
                list.add(token);
            }
        }
        return list;
    }

    /**
     * convert a String with the following format to String Array:
     * string1,string2,string3,...
     * The String Array contains: string1, string2, string3, ...
     *
     * @param arrayString - the String to convert
     * @param sep the separator character
     * @return String[] containing the elements in String
     */
    // package-private, for testing
    static String[] convertStringToStringArray(String arrayString, char sep) {
        final ParamTokenizer paramTok = new ParamTokenizer(arrayString, sep);
        List<String> strs = new ArrayList<String>();
        while (paramTok.hasMoreTokens())
            strs.add(paramTok.nextToken());
        return strs.toArray(new String[strs.size()]);
    }

    /**
     * Check if the value string is one of the strings in the list of
     * acceptable values in the @Param annotation on the target.
     *
     * @param target the target field
     * @param paramValueStr the parameter value
     */
    private static void checkAgainstAcceptableValues(AnnotatedElement target, String paramValueStr) {
        Param param = target.getAnnotation(Param.class);
        String acceptable = param.acceptableValues();
        String paramName = CommandModel.getParamName(param, target);

        if (ok(acceptable) && ok(paramValueStr)) {
            String[] ss = acceptable.split(",");

            for (String s : ss) {
                if (paramValueStr.equals(s.trim()))
                    return; // matched, value is good
            }

            // didn't match any, error
            throw new UnacceptableValueException(localStrings.getLocalString("UnacceptableValue",
                    "Invalid parameter: {0}.  Its value is {1} "
                            + "but it isn''t one of these acceptable values: {2}",
                    paramName, paramValueStr, acceptable));
        }
    }

    private static boolean ok(String s) {
        return s != null && s.length() > 0;
    }
}