1. /*
  2. * @(#)PrintServiceLookup.java 1.14 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.print;
  8. import java.util.ArrayList;
  9. import java.util.Iterator;
  10. import java.util.List;
  11. import javax.print.attribute.AttributeSet;
  12. import sun.awt.AppContext;
  13. import sun.misc.Service;
  14. /** Implementations of this class provide lookup services for
  15. * print services (typically equivalent to printers) of a particular type.
  16. * <p>
  17. * Multiple implementations may be installed concurrently.
  18. * All implementations must be able to describe the located printers
  19. * as instances of a PrintService.
  20. * Typically implementations of this service class are located
  21. * automatically in JAR files (see the SPI JAR file specification).
  22. * These classes must be instantiable using a default constructor.
  23. * Alternatively applications may explicitly register instances
  24. * at runtime.
  25. * <p>
  26. * Applications use only the static methods of this abstract class.
  27. * The instance methods are implemented by a service provider in a subclass
  28. * and the unification of the results from all installed lookup classes
  29. * are reported by the static methods of this class when called by
  30. * the application.
  31. * <p>
  32. * A PrintServiceLookup implementor is recommended to check for the
  33. * SecurityManager.checkPrintJobAccess() to deny access to untrusted code.
  34. * Following this recommended policy means that untrusted code may not
  35. * be able to locate any print services. Downloaded applets are the most
  36. * common example of untrusted code.
  37. * <p>
  38. * This check is made on a per lookup service basis to allow flexibility in
  39. * the policy to reflect the needs of different lookup services.
  40. * <p>
  41. * Services which are registered by registerService(PrintService)
  42. * will not be included in lookup results if a security manager is
  43. * installed and its checkPrintJobAccess() method denies access.
  44. */
  45. public abstract class PrintServiceLookup {
  46. static class Services {
  47. private ArrayList listOfLookupServices = null;
  48. private ArrayList registeredServices = null;
  49. }
  50. private static Services getServicesForContext() {
  51. Services services =
  52. (Services)AppContext.getAppContext().get(Services.class);
  53. if (services == null) {
  54. services = new Services();
  55. AppContext.getAppContext().put(Services.class, services);
  56. }
  57. return services;
  58. }
  59. private static ArrayList getListOfLookupServices() {
  60. return getServicesForContext().listOfLookupServices;
  61. }
  62. private static ArrayList initListOfLookupServices() {
  63. ArrayList listOfLookupServices = new ArrayList();
  64. getServicesForContext().listOfLookupServices = listOfLookupServices;
  65. return listOfLookupServices;
  66. }
  67. private static ArrayList getRegisteredServices() {
  68. return getServicesForContext().registeredServices;
  69. }
  70. private static ArrayList initRegisteredServices() {
  71. ArrayList registeredServices = new ArrayList();
  72. getServicesForContext().registeredServices = registeredServices;
  73. return registeredServices;
  74. }
  75. /**
  76. * Locates print services capable of printing the specified
  77. * {@link DocFlavor}.
  78. *
  79. * @param flavor the flavor to print. If null, this constraint is not
  80. * used.
  81. * @param attributes attributes that the print service must support.
  82. * If null this constraint is not used.
  83. *
  84. * @return array of matching <code>PrintService</code> objects
  85. * representing print services that support the specified flavor
  86. * attributes. If no services match, the array is zero-length.
  87. */
  88. public static final PrintService[]
  89. lookupPrintServices(DocFlavor flavor,
  90. AttributeSet attributes) {
  91. ArrayList list = getServices(flavor, attributes);
  92. return (PrintService[])(list.toArray(new PrintService[list.size()]));
  93. }
  94. /**
  95. * Locates MultiDoc print Services capable of printing MultiDocs
  96. * containing all the specified doc flavors.
  97. * <P> This method is useful to help locate a service that can print
  98. * a <code>MultiDoc</code> in which the elements may be different
  99. * flavors. An application could perform this itself by multiple lookups
  100. * on each <code>DocFlavor</code> in turn and collating the results,
  101. * but the lookup service may be able to do this more efficiently.
  102. *
  103. * @param flavors the flavors to print. If null or empty this
  104. * constraint is not used.
  105. * Otherwise return only multidoc print services that can print all
  106. * specified doc flavors.
  107. * @param attributes attributes that the print service must
  108. * support. If null this constraint is not used.
  109. *
  110. * @return array of matching {@link MultiDocPrintService} objects.
  111. * If no services match, the array is zero-length.
  112. *
  113. */
  114. public static final MultiDocPrintService[]
  115. lookupMultiDocPrintServices(DocFlavor[] flavors,
  116. AttributeSet attributes) {
  117. ArrayList list = getMultiDocServices(flavors, attributes);
  118. return (MultiDocPrintService[])
  119. list.toArray(new MultiDocPrintService[list.size()]);
  120. }
  121. /**
  122. * Locates the default print service for this environment.
  123. * This may return null.
  124. * If multiple lookup services each specify a default, the
  125. * chosen service is not precisely defined, but a
  126. * platform native service, rather than an installed service,
  127. * is usually returned as the default. If there is no clearly
  128. * identifiable
  129. * platform native default print service, the default is the first
  130. * to be located in an implementation-dependent manner.
  131. * <p>
  132. * This may include making use of any preferences API that is available
  133. * as part of the Java or native platform.
  134. * This algorithm may be overridden by a user setting the property
  135. * javax.print.defaultPrinter.
  136. * A service specified must be discovered to be valid and currently
  137. * available to be returned as the default.
  138. *
  139. * @return the default PrintService.
  140. */
  141. public static final PrintService lookupDefaultPrintService() {
  142. Iterator psIterator = getAllLookupServices().iterator();
  143. while (psIterator.hasNext()) {
  144. try {
  145. PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
  146. PrintService service = lus.getDefaultPrintService();
  147. if (service != null) {
  148. return service;
  149. }
  150. } catch (Exception e) {
  151. }
  152. }
  153. return null;
  154. }
  155. /**
  156. * Allows an application to explicitly register a class that
  157. * implements lookup services. The registration will not persist
  158. * across VM invocations.
  159. * This is useful if an application needs to make a new service
  160. * available that is not part of the installation.
  161. * If the lookup service is already registered, or cannot be registered,
  162. * the method returns false.
  163. * <p>
  164. *
  165. * @param sp an implementation of a lookup service.
  166. * @return <code>true</code> if the new lookup service is newly
  167. * registered; <code>false</code> otherwise.
  168. */
  169. public static boolean registerServiceProvider(PrintServiceLookup sp) {
  170. synchronized (PrintServiceLookup.class) {
  171. Iterator psIterator = getAllLookupServices().iterator();
  172. while (psIterator.hasNext()) {
  173. try {
  174. Object lus = psIterator.next();
  175. if (lus.getClass() == sp.getClass()) {
  176. return false;
  177. }
  178. } catch (Exception e) {
  179. }
  180. }
  181. getListOfLookupServices().add(sp);
  182. return true;
  183. }
  184. }
  185. /**
  186. * Allows an application to directly register an instance of a
  187. * class which implements a print service.
  188. * The lookup operations for this service will be
  189. * performed by the PrintServiceLookup class using the attribute
  190. * values and classes reported by the service.
  191. * This may be less efficient than a lookup
  192. * service tuned for that service.
  193. * Therefore registering a <code>PrintServiceLookup</code> instance
  194. * instead is recommended.
  195. * The method returns true if this service is not previously
  196. * registered and is now successfully registered.
  197. * This method should not be called with StreamPrintService instances.
  198. * They will always fail to register and the method will return false.
  199. * @param service an implementation of a print service.
  200. * @return <code>true</code> if the service is newly
  201. * registered; <code>false</code> otherwise.
  202. */
  203. public static boolean registerService(PrintService service) {
  204. synchronized (PrintServiceLookup.class) {
  205. if (service instanceof StreamPrintService) {
  206. return false;
  207. }
  208. ArrayList registeredServices = getRegisteredServices();
  209. if (registeredServices == null) {
  210. registeredServices = initRegisteredServices();
  211. }
  212. else {
  213. if (registeredServices.contains(service)) {
  214. return false;
  215. }
  216. }
  217. registeredServices.add(service);
  218. return true;
  219. }
  220. }
  221. /**
  222. * Locates services that can be positively confirmed to support
  223. * the combination of attributes and DocFlavors specified.
  224. * This method is not called directly by applications.
  225. * <p>
  226. * Implemented by a service provider, used by the static methods
  227. * of this class.
  228. * <p>
  229. * The results should be the same as obtaining all the PrintServices
  230. * and querying each one individually on its support for the
  231. * specified attributes and flavors, but the process can be more
  232. * efficient by taking advantage of the capabilities of lookup services
  233. * for the print services.
  234. *
  235. * @param flavor of document required. If null it is ignored.
  236. * @param attributes required to be supported. If null this
  237. * constraint is not used.
  238. * @return array of matching PrintServices. If no services match, the
  239. * array is zero-length.
  240. */
  241. public abstract PrintService[] getPrintServices(DocFlavor flavor,
  242. AttributeSet attributes);
  243. /**
  244. * Not called directly by applications.
  245. * Implemented by a service provider, used by the static methods
  246. * of this class.
  247. * @return array of all PrintServices known to this lookup service
  248. * class. If none are found, the array is zero-length.
  249. */
  250. public abstract PrintService[] getPrintServices() ;
  251. /**
  252. * Not called directly by applications.
  253. * <p>
  254. * Implemented by a service provider, used by the static methods
  255. * of this class.
  256. * <p>
  257. * Locates MultiDoc print services which can be positively confirmed
  258. * to support the combination of attributes and DocFlavors specified.
  259. * <p>
  260. *
  261. * @param flavors of documents required. If null or empty it is ignored.
  262. * @param attributes required to be supported. If null this
  263. * constraint is not used.
  264. * @return array of matching PrintServices. If no services match, the
  265. * array is zero-length.
  266. */
  267. public abstract MultiDocPrintService[]
  268. getMultiDocPrintServices(DocFlavor[] flavors,
  269. AttributeSet attributes);
  270. /**
  271. * Not called directly by applications.
  272. * Implemented by a service provider, and called by the print lookup
  273. * service
  274. * @return the default PrintService for this lookup service.
  275. * If there is no default, returns null.
  276. */
  277. public abstract PrintService getDefaultPrintService();
  278. private static ArrayList getAllLookupServices() {
  279. synchronized (PrintServiceLookup.class) {
  280. ArrayList listOfLookupServices = getListOfLookupServices();
  281. if (listOfLookupServices != null) {
  282. return listOfLookupServices;
  283. } else {
  284. listOfLookupServices = initListOfLookupServices();
  285. }
  286. try {
  287. java.security.AccessController.doPrivileged(
  288. new java.security.PrivilegedExceptionAction() {
  289. public Object run() {
  290. Iterator iterator =
  291. Service.providers(PrintServiceLookup.class);
  292. ArrayList los = getListOfLookupServices();
  293. while (iterator.hasNext()) {
  294. try {
  295. PrintServiceLookup lus =
  296. (PrintServiceLookup)iterator.next();
  297. los.add(lus);
  298. } catch (Exception e) {
  299. }
  300. }
  301. return null;
  302. }
  303. });
  304. } catch (java.security.PrivilegedActionException e) {
  305. }
  306. return listOfLookupServices;
  307. }
  308. }
  309. private static ArrayList getServices(DocFlavor flavor,
  310. AttributeSet attributes) {
  311. ArrayList listOfServices = new ArrayList();
  312. Iterator psIterator = getAllLookupServices().iterator();
  313. while (psIterator.hasNext()) {
  314. try {
  315. PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
  316. PrintService[] services=null;
  317. if (flavor == null && attributes == null) {
  318. try {
  319. services = lus.getPrintServices();
  320. } catch (Throwable tr) {
  321. }
  322. } else {
  323. services = lus.getPrintServices(flavor, attributes);
  324. }
  325. if (services == null) {
  326. continue;
  327. }
  328. for (int i=0; i<services.length; i++) {
  329. listOfServices.add(services[i]);
  330. }
  331. } catch (Exception e) {
  332. }
  333. }
  334. /* add any directly registered services */
  335. ArrayList registeredServices = null;
  336. try {
  337. SecurityManager security = System.getSecurityManager();
  338. if (security != null) {
  339. security.checkPrintJobAccess();
  340. }
  341. registeredServices = getRegisteredServices();
  342. } catch (SecurityException se) {
  343. }
  344. if (registeredServices != null) {
  345. PrintService[] services = (PrintService[])
  346. registeredServices.toArray(
  347. new PrintService[registeredServices.size()]);
  348. for (int i=0; i<services.length; i++) {
  349. if (!listOfServices.contains(services[i])) {
  350. if (flavor == null && attributes == null) {
  351. listOfServices.add(services[i]);
  352. } else if (((flavor != null &&
  353. services[i].isDocFlavorSupported(flavor)) ||
  354. flavor == null) &&
  355. null == services[i].getUnsupportedAttributes(
  356. flavor, attributes)) {
  357. listOfServices.add(services[i]);
  358. }
  359. }
  360. }
  361. }
  362. return listOfServices;
  363. }
  364. private static ArrayList getMultiDocServices(DocFlavor[] flavors,
  365. AttributeSet attributes) {
  366. ArrayList listOfServices = new ArrayList();
  367. Iterator psIterator = getAllLookupServices().iterator();
  368. while (psIterator.hasNext()) {
  369. try {
  370. PrintServiceLookup lus = (PrintServiceLookup)psIterator.next();
  371. MultiDocPrintService[] services =
  372. lus.getMultiDocPrintServices(flavors, attributes);
  373. if (services == null) {
  374. continue;
  375. }
  376. for (int i=0; i<services.length; i++) {
  377. listOfServices.add(services[i]);
  378. }
  379. } catch (Exception e) {
  380. }
  381. }
  382. /* add any directly registered services */
  383. ArrayList registeredServices = null;
  384. try {
  385. SecurityManager security = System.getSecurityManager();
  386. if (security != null) {
  387. security.checkPrintJobAccess();
  388. }
  389. registeredServices = getRegisteredServices();
  390. } catch (Exception e) {
  391. }
  392. if (registeredServices != null) {
  393. PrintService[] services = (PrintService[])
  394. registeredServices.toArray(
  395. new PrintService[registeredServices.size()]);
  396. for (int i=0; i<services.length; i++) {
  397. if (services[i] instanceof MultiDocPrintService &&
  398. !listOfServices.contains(services[i])) {
  399. if (flavors == null || flavors.length == 0) {
  400. listOfServices.add(services[i]);
  401. } else {
  402. boolean supported = true;
  403. for (int f=0; f<flavors.length; f++) {
  404. if (services[i].isDocFlavorSupported(flavors[f])) {
  405. if (services[i].getUnsupportedAttributes(
  406. flavors[f], attributes) != null) {
  407. supported = false;
  408. break;
  409. }
  410. } else {
  411. supported = false;
  412. break;
  413. }
  414. }
  415. if (supported) {
  416. listOfServices.add(services[i]);
  417. }
  418. }
  419. }
  420. }
  421. }
  422. return listOfServices;
  423. }
  424. }