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