1 /*
   2  * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.smartcardio;
  27 
  28 import java.util.*;
  29 
  30 import java.security.*;
  31 
  32 import sun.security.jca.*;
  33 import sun.security.jca.GetInstance.*;
  34 
  35 /**
  36  * A factory for CardTerminal objects.
  37  *
  38  * It allows an application to
  39  * <ul>
  40  * <li>obtain a TerminalFactory by calling
  41  * one of the static factory methods in this class
  42  * ({@linkplain #getDefault} or {@linkplain #getInstance getInstance()}).
  43  * <li>use this TerminalFactory object to access the CardTerminals by
  44  * calling the {@linkplain #terminals} method.
  45  * </ul>
  46  *
  47  * <p>Each TerminalFactory has a <code>type</code> indicating how it
  48  * was implemented. It must be specified when the implementation is obtained
  49  * using a {@linkplain #getInstance getInstance()} method and can be retrieved
  50  * via the {@linkplain #getType} method.
  51  *
  52  * <P>The following standard type names have been defined:
  53  * <dl>
  54  * <dt><code>PC/SC</code>
  55  * <dd>an implementation that calls into the PC/SC Smart Card stack
  56  * of the host platform.
  57  * Implementations do not require parameters and accept "null" as argument
  58  * in the getInstance() calls.
  59  * <dt><code>None</code>
  60  * <dd>an implementation that does not supply any CardTerminals. On platforms
  61  * that do not support other implementations,
  62  * {@linkplain #getDefaultType} returns <code>None</code> and
  63  * {@linkplain #getDefault} returns an instance of a <code>None</code>
  64  * TerminalFactory. Factories of this type cannot be obtained by calling the
  65  * <code>getInstance()</code> methods.
  66  * </dl>
  67  * Additional standard types may be defined in the future.
  68  *
  69  * <p><strong>Note:</strong>
  70  * Provider implementations that accept initialization parameters via the
  71  * <code>getInstance()</code> methods are strongly
  72  * encouraged to use a {@linkplain java.util.Properties} object as the
  73  * representation for String name-value pair based parameters whenever
  74  * possible. This allows applications to more easily interoperate with
  75  * multiple providers than if each provider used different provider
  76  * specific class as parameters.
  77  *
  78  * <P>TerminalFactory utilizes an extensible service provider framework.
  79  * Service providers that wish to add a new implementation should see the
  80  * {@linkplain TerminalFactorySpi} class for more information.
  81  *
  82  * @see CardTerminals
  83  * @see Provider
  84  *
  85  * @since   1.6
  86  * @author  Andreas Sterbenz
  87  * @author  JSR 268 Expert Group
  88  */
  89 public final class TerminalFactory {
  90 
  91     private final static String PROP_NAME =
  92                         "javax.smartcardio.TerminalFactory.DefaultType";
  93 
  94     private final static String defaultType;
  95 
  96     private final static TerminalFactory defaultFactory;
  97 
  98     static {
  99         // lookup up the user specified type, default to PC/SC
 100         String type = AccessController.doPrivileged(
 101              (PrivilegedAction<String>) () -> System.getProperty(PROP_NAME, "PC/SC")).trim();
 102         TerminalFactory factory = null;
 103         try {
 104             factory = TerminalFactory.getInstance(type, null);
 105         } catch (Exception e) {
 106             // ignore
 107         }
 108         if (factory == null) {
 109             // if that did not work, try the Sun PC/SC factory
 110             try {
 111                 type = "PC/SC";
 112                 Provider sun = Security.getProvider("SunPCSC");
 113                 if (sun == null) {
 114                     Class<?> clazz = Class.forName("sun.security.smartcardio.SunPCSC");
 115                     sun = (Provider)clazz.newInstance();
 116                 }
 117                 factory = TerminalFactory.getInstance(type, null, sun);
 118             } catch (Exception e) {
 119                 // ignore
 120             }
 121         }
 122         if (factory == null) {
 123             type = "None";
 124             factory = new TerminalFactory
 125                         (NoneFactorySpi.INSTANCE, NoneProvider.INSTANCE, "None");
 126         }
 127         defaultType = type;
 128         defaultFactory = factory;
 129     }
 130 
 131     private static final class NoneProvider extends Provider {
 132 
 133         private static final long serialVersionUID = 2745808869881593918L;
 134         final static Provider INSTANCE = new NoneProvider();
 135         private NoneProvider() {
 136             super("None", 1.0d, "none");
 137         }
 138     }
 139 
 140     private static final class NoneFactorySpi extends TerminalFactorySpi {
 141         final static TerminalFactorySpi INSTANCE = new NoneFactorySpi();
 142         private NoneFactorySpi() {
 143             // empty
 144         }
 145         protected CardTerminals engineTerminals() {
 146             return NoneCardTerminals.INSTANCE;
 147         }
 148     }
 149 
 150     private static final class NoneCardTerminals extends CardTerminals {
 151         final static CardTerminals INSTANCE = new NoneCardTerminals();
 152         private NoneCardTerminals() {
 153             // empty
 154         }
 155         public List<CardTerminal> list(State state) throws CardException {
 156             if (state == null) {
 157                 throw new NullPointerException();
 158             }
 159             return Collections.emptyList();
 160         }
 161         public boolean waitForChange(long timeout) throws CardException {
 162             throw new IllegalStateException("no terminals");
 163         }
 164     }
 165 
 166     private final TerminalFactorySpi spi;
 167 
 168     private final Provider provider;
 169 
 170     private final String type;
 171 
 172     private TerminalFactory(TerminalFactorySpi spi, Provider provider, String type) {
 173         this.spi = spi;
 174         this.provider = provider;
 175         this.type = type;
 176     }
 177 
 178     /**
 179      * Get the default TerminalFactory type.
 180      *
 181      * <p>It is determined as follows:
 182      *
 183      * when this class is initialized, the system property
 184      * <code>javax.smartcardio.TerminalFactory.DefaultType</code>
 185      * is examined. If it is set, a TerminalFactory of this type is
 186      * instantiated by calling the {@linkplain #getInstance
 187      * getInstance(String,Object)} method passing
 188      * <code>null</code> as the value for <code>params</code>. If the call
 189      * succeeds, the type becomes the default type and the factory becomes
 190      * the {@linkplain #getDefault default} factory.
 191      *
 192      * <p>If the system property is not set or the getInstance() call fails
 193      * for any reason, the system defaults to an implementation specific
 194      * default type and TerminalFactory.
 195      *
 196      * @return the default TerminalFactory type
 197      */
 198     public static String getDefaultType() {
 199         return defaultType;
 200     }
 201 
 202     /**
 203      * Returns the default TerminalFactory instance. See
 204      * {@linkplain #getDefaultType} for more information.
 205      *
 206      * <p>A default TerminalFactory is always available. However, depending
 207      * on the implementation, it may not offer any terminals.
 208      *
 209      * @return the default TerminalFactory
 210      */
 211     public static TerminalFactory getDefault() {
 212         return defaultFactory;
 213     }
 214 
 215     /**
 216      * Returns a TerminalFactory of the specified type that is initialized
 217      * with the specified parameters.
 218      *
 219      * <p> This method traverses the list of registered security Providers,
 220      * starting with the most preferred Provider.
 221      * A new TerminalFactory object encapsulating the
 222      * TerminalFactorySpi implementation from the first
 223      * Provider that supports the specified type is returned.
 224      *
 225      * <p> Note that the list of registered providers may be retrieved via
 226      * the {@linkplain Security#getProviders() Security.getProviders()} method.
 227      *
 228      * <p>The <code>TerminalFactory</code> is initialized with the
 229      * specified parameters Object. The type of parameters
 230      * needed may vary between different types of <code>TerminalFactory</code>s.
 231      *
 232      * @param type the type of the requested TerminalFactory
 233      * @param params the parameters to pass to the TerminalFactorySpi
 234      *   implementation, or null if no parameters are needed
 235      * @return a TerminalFactory of the specified type
 236      *
 237      * @throws NullPointerException if type is null
 238      * @throws NoSuchAlgorithmException if no Provider supports a
 239      *   TerminalFactorySpi of the specified type
 240      */
 241     public static TerminalFactory getInstance(String type, Object params)
 242             throws NoSuchAlgorithmException {
 243         Instance instance = GetInstance.getInstance("TerminalFactory",
 244             TerminalFactorySpi.class, type, params);
 245         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 246             instance.provider, type);
 247     }
 248 
 249     /**
 250      * Returns a TerminalFactory of the specified type that is initialized
 251      * with the specified parameters.
 252      *
 253      * <p> A new TerminalFactory object encapsulating the
 254      * TerminalFactorySpi implementation from the specified provider
 255      * is returned.  The specified provider must be registered
 256      * in the security provider list.
 257      *
 258      * <p> Note that the list of registered providers may be retrieved via
 259      * the {@linkplain Security#getProviders() Security.getProviders()} method.
 260      *
 261      * <p>The <code>TerminalFactory</code> is initialized with the
 262      * specified parameters Object. The type of parameters
 263      * needed may vary between different types of <code>TerminalFactory</code>s.
 264      *
 265      * @param type the type of the requested TerminalFactory
 266      * @param params the parameters to pass to the TerminalFactorySpi
 267      *   implementation, or null if no parameters are needed
 268      * @param provider the name of the provider
 269      * @return a TerminalFactory of the specified type
 270      *
 271      * @throws NullPointerException if type is null
 272      * @throws IllegalArgumentException if provider is null or the empty String
 273      * @throws NoSuchAlgorithmException if a TerminalFactorySpi implementation
 274      *   of the specified type is not available from the specified provider
 275      * @throws NoSuchAlgorithmException if no TerminalFactory of the
 276      *   specified type could be found
 277      * @throws NoSuchProviderException if the specified provider could not
 278      *   be found
 279      */
 280     public static TerminalFactory getInstance(String type, Object params,
 281             String provider) throws NoSuchAlgorithmException, NoSuchProviderException {
 282         Instance instance = GetInstance.getInstance("TerminalFactory",
 283             TerminalFactorySpi.class, type, params, provider);
 284         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 285             instance.provider, type);
 286     }
 287 
 288     /**
 289      * Returns a TerminalFactory of the specified type that is initialized
 290      * with the specified parameters.
 291      *
 292      * <p> A new TerminalFactory object encapsulating the
 293      * TerminalFactorySpi implementation from the specified provider object
 294      * is returned. Note that the specified provider object does not have to be
 295      * registered in the provider list.
 296      *
 297      * <p>The <code>TerminalFactory</code> is initialized with the
 298      * specified parameters Object. The type of parameters
 299      * needed may vary between different types of <code>TerminalFactory</code>s.
 300      *
 301      * @param type the type of the requested TerminalFactory
 302      * @param params the parameters to pass to the TerminalFactorySpi
 303      *   implementation, or null if no parameters are needed
 304      * @param provider the provider
 305      * @return a TerminalFactory of the specified type
 306      *
 307      * @throws NullPointerException if type is null
 308      * @throws IllegalArgumentException if provider is null
 309      * @throws NoSuchAlgorithmException if a TerminalFactorySpi implementation
 310      *   of the specified type is not available from the specified Provider
 311      */
 312     public static TerminalFactory getInstance(String type, Object params,
 313             Provider provider) throws NoSuchAlgorithmException {
 314         Instance instance = GetInstance.getInstance("TerminalFactory",
 315             TerminalFactorySpi.class, type, params, provider);
 316         return new TerminalFactory((TerminalFactorySpi)instance.impl,
 317             instance.provider, type);
 318     }
 319 
 320     /**
 321      * Returns the provider of this TerminalFactory.
 322      *
 323      * @return the provider of this TerminalFactory.
 324      */
 325     public Provider getProvider() {
 326         return provider;
 327     }
 328 
 329     /**
 330      * Returns the type of this TerminalFactory. This is the value that was
 331      * specified in the getInstance() method that returned this object.
 332      *
 333      * @return the type of this TerminalFactory
 334      */
 335     public String getType() {
 336         return type;
 337     }
 338 
 339     /**
 340      * Returns a new CardTerminals object encapsulating the terminals
 341      * supported by this factory.
 342      * See the class comment of the {@linkplain CardTerminals} class
 343      * regarding how the returned objects can be shared and reused.
 344      *
 345      * @return a new CardTerminals object encapsulating the terminals
 346      * supported by this factory.
 347      */
 348     public CardTerminals terminals() {
 349         return spi.engineTerminals();
 350     }
 351 
 352     /**
 353      * Returns a string representation of this TerminalFactory.
 354      *
 355      * @return a string representation of this TerminalFactory.
 356      */
 357     public String toString() {
 358         return "TerminalFactory for type " + type + " from provider "
 359             + provider.getName();
 360     }
 361 
 362 }