View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.vfs.util;
    18
    19  import org.apache.commons.logging.Log;
    20  import org.apache.commons.logging.LogFactory;
    21  import org.apache.commons.vfs.FileSystemConfigBuilder;
    22  import org.apache.commons.vfs.FileSystemException;
    23  import org.apache.commons.vfs.FileSystemManager;
    24  import org.apache.commons.vfs.FileSystemOptions;
    25
    26  import java.lang.reflect.Array;
    27  import java.lang.reflect.Constructor;
    28  import java.lang.reflect.InvocationTargetException;
    29  import java.lang.reflect.Method;
    30  import java.lang.reflect.Modifier;
    31  import java.util.ArrayList;
    32  import java.util.Iterator;
    33  import java.util.List;
    34  import java.util.Map;
    35  import java.util.TreeMap;
    36
    37  /***
    38   * This class use reflection to set a configuration value using the fileSystemConfigBuilder
    39   * associated the a scheme.<br><br>
    40   * Example:<br>
    41   * <pre>
    42   * FileSystemOptions fso = new FileSystemOptions();
    43   * DelegatingFileSystemOptionsBuilder delegate = new DelegatingFileSystemOptionsBuilder(VFS.getManager());
    44   * delegate.setConfigString(fso, "sftp", "identities", "c:/tmp/test.ident");
    45   * delegate.setConfigString(fso, "http", "proxyPort", "8080");
    46   * delegate.setConfigClass(fso, "sftp", "userinfo", TrustEveryoneUserInfo.class);
    47   * </pre>
    48   *
    49   * @author <a href="mailto:imario@apache.org">Mario Ivankovits</a>
    50   * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
    51   */
    52  public class DelegatingFileSystemOptionsBuilder
    53  {
    54      private Log log = LogFactory.getLog(DelegatingFileSystemOptionsBuilder.class);
    55
    56      private final static Class[] STRING_PARAM = new Class[]{String.class};
    57
    58      private final FileSystemManager manager;
    59
    60      private final Map beanMethods = new TreeMap();
    61
    62      private final static Map primitiveToObject = new TreeMap();
    63
    64      static
    65      {
    66          primitiveToObject.put(Void.TYPE.getName(), Void.class);
    67          primitiveToObject.put(Boolean.TYPE.getName(), Boolean.class);
    68          primitiveToObject.put(Byte.TYPE.getName(), Byte.class);
    69          primitiveToObject.put(Character.TYPE.getName(), Character.class);
    70          primitiveToObject.put(Short.TYPE.getName(), Short.class);
    71          primitiveToObject.put(Integer.TYPE.getName(), Integer.class);
    72          primitiveToObject.put(Long.TYPE.getName(), Long.class);
    73          primitiveToObject.put(Double.TYPE.getName(), Double.class);
    74          primitiveToObject.put(Float.TYPE.getName(), Float.class);
    75      }
    76
    77      private static class Context
    78      {
    79          private final FileSystemOptions fso;
    80          private final String scheme;
    81          private final String name;
    82          private final Object[] values;
    83
    84          private List configSetters;
    85          private FileSystemConfigBuilder fileSystemConfigBuilder;
    86
    87          private Context(final FileSystemOptions fso, final String scheme, final String name, final Object[] values)
    88          {
    89              this.fso = fso;
    90              this.scheme = scheme;
    91              this.name = name;
    92              this.values = values;
    93          }
    94      }
    95
    96      /***
    97       * Constructor.<br>
    98       * Pass in your fileSystemManager instance.
    99       *
    100      * @param manager the manager to use to get the fileSystemConfigBuilder assocated to a scheme
    101      */
    102     public DelegatingFileSystemOptionsBuilder(final FileSystemManager manager)
    103     {
    104         this.manager = manager;
    105     }
    106
    107     protected FileSystemManager getManager()
    108     {
    109         return manager;
    110     }
    111
    112     /***
    113      * Set a single string value.
    114      *
    115      * @param fso    FileSystemOptions
    116      * @param scheme scheme
    117      * @param name   name
    118      * @param value  value
    119      */
    120     public void setConfigString(final FileSystemOptions fso, final String scheme, final String name, final String value) throws FileSystemException
    121     {
    122         setConfigStrings(fso, scheme, name, new String[]{value});
    123     }
    124
    125     /***
    126      * Set an array of string value.
    127      *
    128      * @param fso    FileSystemOptions
    129      * @param scheme scheme
    130      * @param name   name
    131      * @param values values
    132      */
    133     public void setConfigStrings(final FileSystemOptions fso, final String scheme, final String name, final String[] values) throws FileSystemException
    134     {
    135         Context ctx = new Context(fso, scheme, name, values);
    136
    137         setValues(ctx);
    138     }
    139
    140     /***
    141      * Set a single class value.<br>
    142      * The class has to implement a no-args constructor, else the instantiation might fail.
    143      *
    144      * @param fso       FileSystemOptions
    145      * @param scheme    scheme
    146      * @param name      name
    147      * @param className className
    148      */
    149     public void setConfigClass(final FileSystemOptions fso, final String scheme, final String name, final Class className) throws FileSystemException, IllegalAccessException, InstantiationException
    150     {
    151         setConfigClasses(fso, scheme, name, new Class[]{className});
    152     }
    153
    154     /***
    155      * Set an array of class values.<br>
    156      * The class has to implement a no-args constructor, else the instantiation might fail.
    157      *
    158      * @param fso        FileSystemOptions
    159      * @param scheme     scheme
    160      * @param name       name
    161      * @param classNames classNames
    162      */
    163     public void setConfigClasses(final FileSystemOptions fso, final String scheme, final String name, final Class[] classNames) throws FileSystemException, IllegalAccessException, InstantiationException
    164     {
    165         Object values[] = new Object[classNames.length];
    166         for (int iterClassNames = 0; iterClassNames < values.length; iterClassNames++)
    167         {
    168             values[iterClassNames] = classNames[iterClassNames].newInstance();
    169         }
    170
    171         Context ctx = new Context(fso, scheme, name, values);
    172
    173         setValues(ctx);
    174     }
    175
    176     /***
    177      * sets the values using the informations of the given context.<br>
    178      */
    179     private void setValues(Context ctx) throws FileSystemException
    180     {
    181         // find all setter methods suitable for the given "name"
    182         if (!fillConfigSetters(ctx))
    183         {
    184             throw new FileSystemException("vfs.provider/config-key-invalid.error", new String[]
    185             {
    186                 ctx.scheme,
    187                 ctx.name
    188             });
    189         }
    190
    191         // get the fileSystemConfigBuilder
    192         ctx.fileSystemConfigBuilder = getManager().getFileSystemConfigBuilder(ctx.scheme);
    193
    194         // try to find a setter which could accept the value
    195         Iterator iterConfigSetters = ctx.configSetters.iterator();
    196         while (iterConfigSetters.hasNext())
    197         {
    198             Method configSetter = (Method) iterConfigSetters.next();
    199             if (convertValuesAndInvoke(configSetter, ctx))
    200             {
    201                 return;
    202             }
    203         }
    204
    205         throw new FileSystemException("vfs.provider/config-value-invalid.error", new Object[]
    206         {
    207             ctx.scheme,
    208             ctx.name,
    209             ctx.values
    210         });
    211     }
    212
    213     /***
    214      * tries to convert the value and pass it to the given method
    215      */
    216     private boolean convertValuesAndInvoke(final Method configSetter, final Context ctx) throws FileSystemException
    217     {
    218         Class parameters[] = configSetter.getParameterTypes();
    219         if (parameters.length < 2)
    220         {
    221             return false;
    222         }
    223         if (!parameters[0].isAssignableFrom(FileSystemOptions.class))
    224         {
    225             return false;
    226         }
    227
    228         Class valueParameter = parameters[1];
    229         Class type;
    230         if (valueParameter.isArray())
    231         {
    232             type = valueParameter.getComponentType();
    233         }
    234         else
    235         {
    236             if (ctx.values.length > 1)
    237             {
    238                 return false;
    239             }
    240
    241             type = valueParameter;
    242         }
    243
    244         if (type.isPrimitive())
    245         {
    246             Class objectType = (Class) primitiveToObject.get(type.getName());
    247             if (objectType == null)
    248             {
    249                 log.warn(Messages.getString("vfs.provider/config-unexpected-primitive.error", type.getName()));
    250                 return false;
    251             }
    252             type = objectType;
    253         }
    254
    255         Class valueClass = ctx.values[0].getClass();
    256         if (type.isAssignableFrom(valueClass))
    257         {
    258             // can set value directly
    259             invokeSetter(valueParameter, ctx, configSetter, ctx.values);
    260             return true;
    261         }
    262         if (valueClass != String.class)
    263         {
    264             log.warn(Messages.getString("vfs.provider/config-unexpected-value-class.error", new String[]
    265             {
    266                 valueClass.getName(),
    267                 ctx.scheme,
    268                 ctx.name
    269             }));
    270             return false;
    271         }
    272
    273         Object convertedValues = java.lang.reflect.Array.newInstance(type, ctx.values.length);
    274
    275         Constructor valueConstructor;
    276         try
    277         {
    278             valueConstructor = type.getConstructor(STRING_PARAM);
    279         }
    280         catch (NoSuchMethodException e)
    281         {
    282             valueConstructor = null;
    283         }
    284         if (valueConstructor != null)
    285         {
    286             // can convert using constructor
    287             for (int iterValues = 0; iterValues < ctx.values.length; iterValues++)
    288             {
    289                 try
    290                 {
    291                     Array.set(convertedValues, iterValues, valueConstructor.newInstance(new Object[]{ctx.values[iterValues]}));
    292                 }
    293                 catch (InstantiationException e)
    294                 {
    295                     throw new FileSystemException(e);
    296                 }
    297                 catch (IllegalAccessException e)
    298                 {
    299                     throw new FileSystemException(e);
    300                 }
    301                 catch (InvocationTargetException e)
    302                 {
    303                     throw new FileSystemException(e);
    304                 }
    305             }
    306
    307             invokeSetter(valueParameter, ctx, configSetter, convertedValues);
    308             return true;
    309         }
    310
    311         Method valueFactory;
    312         try
    313         {
    314             valueFactory = type.getMethod("valueOf", STRING_PARAM);
    315             if (!Modifier.isStatic(valueFactory.getModifiers()))
    316             {
    317                 valueFactory = null;
    318             }
    319         }
    320         catch (NoSuchMethodException e)
    321         {
    322             valueFactory = null;
    323         }
    324
    325         if (valueFactory != null)
    326         {
    327             // can convert using factory method (valueOf)
    328             for (int iterValues = 0; iterValues < ctx.values.length; iterValues++)
    329             {
    330                 try
    331                 {
    332                     Array.set(convertedValues, iterValues, valueFactory.invoke(null, new Object[]{ctx.values[iterValues]}));
    333                 }
    334                 catch (IllegalAccessException e)
    335                 {
    336                     throw new FileSystemException(e);
    337                 }
    338                 catch (InvocationTargetException e)
    339                 {
    340                     throw new FileSystemException(e);
    341                 }
    342             }
    343
    344             invokeSetter(valueParameter, ctx, configSetter, convertedValues);
    345             return true;
    346         }
    347
    348         return false;
    349     }
    350
    351     /***
    352      * invokes the method with the converted values
    353      */
    354     private void invokeSetter(Class valueParameter, final Context ctx, final Method configSetter, final Object values)
    355         throws FileSystemException
    356     {
    357         Object[] args;
    358         if (valueParameter.isArray())
    359         {
    360             args = new Object[]
    361             {
    362                 ctx.fso,
    363                 values
    364             };
    365         }
    366         else
    367         {
    368             args = new Object[]
    369             {
    370                 ctx.fso,
    371                 Array.get(values, 0)
    372             };
    373         }
    374         try
    375         {
    376             configSetter.invoke(ctx.fileSystemConfigBuilder, args);
    377         }
    378         catch (IllegalAccessException e)
    379         {
    380             throw new FileSystemException(e);
    381         }
    382         catch (InvocationTargetException e)
    383         {
    384             throw new FileSystemException(e);
    385         }
    386     }
    387
    388     /***
    389      * fills all available set*() methods for the context-scheme into the context.
    390      */
    391     private boolean fillConfigSetters(final Context ctx)
    392         throws FileSystemException
    393     {
    394         Map schemeMethods = getSchemeMethods(ctx.scheme);
    395         List configSetters = (List) schemeMethods.get(ctx.name.toLowerCase());
    396         if (configSetters == null)
    397         {
    398             return false;
    399         }
    400
    401         ctx.configSetters = configSetters;
    402         return true;
    403     }
    404
    405     /***
    406      * get (cached) list of set*() methods for the given scheme
    407      */
    408     private Map getSchemeMethods(final String scheme) throws FileSystemException
    409     {
    410         Map schemeMethods = (Map) beanMethods.get(scheme);
    411         if (schemeMethods == null)
    412         {
    413             schemeMethods = createSchemeMethods(scheme);
    414             beanMethods.put(scheme, schemeMethods);
    415         }
    416
    417         return schemeMethods;
    418     }
    419
    420     /***
    421      * create the list of all set*() methods for the given scheme
    422      */
    423     private Map createSchemeMethods(String scheme) throws FileSystemException
    424     {
    425         final FileSystemConfigBuilder fscb = getManager().getFileSystemConfigBuilder(scheme);
    426         if (fscb == null)
    427         {
    428             throw new FileSystemException("vfs.provider/no-config-builder.error", scheme);
    429         }
    430
    431         Map schemeMethods = new TreeMap();
    432
    433         Method methods[] = fscb.getClass().getMethods();
    434         for (int iterMethods = 0; iterMethods < methods.length; iterMethods++)
    435         {
    436             Method method = methods[iterMethods];
    437             if (!Modifier.isPublic(method.getModifiers()))
    438             {
    439                 continue;
    440             }
    441
    442             String methodName = method.getName();
    443             if (!methodName.startsWith("set"))
    444             {
    445                 // not a setter
    446                 continue;
    447             }
    448
    449             String key = methodName.substring(3).toLowerCase();
    450
    451             List configSetter = (List) schemeMethods.get(key);
    452             if (configSetter == null)
    453             {
    454                 configSetter = new ArrayList(2);
    455                 schemeMethods.put(key, configSetter);
    456             }
    457             configSetter.add(method);
    458         }
    459
    460         return schemeMethods;
    461     }
    462 }