Friday, November 24, 2006
Wednesday, November 22, 2006
ZK AJAX
Ajax Web framework that enables rich user interface for Web applications with no JavaScript and little programming.
Google Web Toolkit
La característica principal de esta versión es que por fin se puede decir que es multiplataforma al añadir soporte para el Mac OS X , por lo que los widgets creados podrán ejecutarse en Mozilla, IExplorer, Opera y Safari. Existe una entrevista al equipo de desarrollo de GWT en InfoQ donde hablan más sobre este tema.
Esta versión incluye también mejoras al desempeño en modo de debug, ahora es mucho más rápido usar este modo y arreglos a errores reportados por los usarios.
Desde el inicio de este proyecto, GWT ha atraido a muchos desarrolladores web. Por ahora, ya existe soporte en IDEA 6.0, un plugin de pago para Eclipse y un editor standalone (con versiones gratuitas y de pago); un libro en camino y librerías de widgets creados por terceros . Nada mal para un proyecto que se dió a conocer apenas en Mayo de este año. ¿Qué piensan ustedes del futuro de GWT? ¿Han usado esta herramienta en sus desarrollos?
Monday, October 30, 2006
Writing a simple bean
In this section you will learn more about Beans and the
BeanBox by
- Creating a simple Bean
- Compiling and saving the Bean into a
Java Archive (JAR) file
- Loading the Bean into the ToolBox
- Dropping a Bean instance into the BeanBox
- Inspecting the Bean's properties, methods, and events
- Generating an introspection report
Your Bean will be namedSimpleBean.
Here are the steps to create it and view it in the BeanBox:
- Write the
SimpleBeancode. Put it in a file
namedSimpleBean.java, in the directory
of your choice. Here's the code:
import java.awt.*;
import java.io.Serializable;
public class SimpleBean extends Canvas
implements Serializable
{
//Constructor sets inherited properties
public SimpleBean(){
setSize(60,40);
setBackground(Color.red);
}
}
SimpleBeanextends thejava.awt.Canvascomponent.SimpleBeanalso implements thejava.io.Serializableinterface,
a requirement for all Beans.SimpleBeansets the background
color and component size.
- Make sure the
CLASSPATH
environment variable is set to point to all
needed.class(or.jar) files. Here
are some URLs that will help you to set CLASSPATH correctly:
- The
Managing Source and Class Files lesson gives good advice on how and when to set your CLASSPATH.
- The JDK Tool Reference Page provides complete CLASSPATH information for both
Windows and
Solaris platforms.
- Compile the Bean:
javac SimpleBean.java
This produces the class fileSimpleBean.class
- Create a manifest file. Use your favorite text editor
to create a file, we'll call itmanifest.tmp,
that contains the following text:
Name: SimpleBean.class
Java-Bean: True
- Create the JAR file. The JAR file will contain the
manifest and theSimpleBeanclass file:
jar cfm SimpleBean.jar manifest.tmp SimpleBean.class
See the
Packaging Programs in JAR Files trail, and the
JDK JAR file documentation for complete information on JAR files.
- Load the JAR file into the ToolBox. Select
the File|LoadJar... menu item. This will bring up a
file browser. Navigate to theSimpleBean.jarlocation and
select it.SimpleBeanwill appear at the bottom
of the ToolBox. (Note that when the BeanBox is
started, all Beans in JAR files in the
beans/jarsdirectory are automatically
loaded into the ToolBox).
- Drop a
SimpleBeaninstance into the BeanBox.
Click on the wordSimpleBeanin the ToolBox. The cursor
will change to a crosshair. Move the cursor to a spot within the
BeanBox and click. SimpleBean will appear as a
painted rectangle with a hatched border. This border means
thatSimpleBeanis selected. The
SimpleBeanproperties will appear in the
Properties sheet.
You can resizeSimpleBean, because it inherits fromCanvas, by dragging a corner. You will see the cursor
change to a right angle when over a corner. You can also repositionSimpleBeanwithin the BeanBox by dragging on any non-corner
portion of the hatched border. You will see the cursor change to crossed
arrows when in position to move the Bean.
SimpleBean Makefiles
Below are two makefiles (Unix and Windows) set up to
createSimpleBean.
# gnumake file
CLASSFILES= SimpleBean.class
JARFILE= SimpleBean.jar
all: $(JARFILE)
# Create a JAR file with a suitable manifest.
$(JARFILE): $(CLASSFILES) $(DATAFILES)
echo "Name: SimpleBean.class" >> manifest.tmp
echo "Java-Bean: True" >> manifest.tmp
jar cfm $(JARFILE) manifest.tmp *.class
@/bin/rm manifest.tmp
# Compile the sources
%.class: %.java
export CLASSPATH; CLASSPATH=. ; javac $<
# make clean
clean:
/bin/rm -f *.class
/bin/rm -f $(JARFILE)
Here is the Windowsnmakeversion:
# nmake file
CLASSFILES= simplebean.class
JARFILE= simplebean.jar
all: $(JARFILE)
# Create a JAR file with a suitable manifest.
$(JARFILE): $(CLASSFILES) $(DATAFILES)
jar cfm $(JARFILE) <<manifest.tmp *.class
Name: SimpleBean.class
Java-Bean: True
<<
.SUFFIXES: .java .class
{sunw\demo\simple}.java{sunw\demo\simple}.class :
set CLASSPATH=.
javac $<
clean:
-del sunw\demo\simple\*.class
-del $(JARFILE)
You can use these makefiles as templates for creating your own
Bean makefiles. The example Bean makefiles, in thebeans/demodirectory, also show you how to use
makefiles to build and maintain your Beans.
Inspecting SimpleBean Properties and Events
The Properties sheet displays the selected Bean's properties.
WithSimpleBeanselected, the Properties
sheet displays four propeties:foreground,background,font, andname.
We declared no properties inSimpleBean
(see the Properties section to learn
how to declare properties), so these are properties inherited fromCanvas. Clicking on each property brings up a property
editor. The BeanBox provides default property editors
for the primitive types, plusFontandColor
types. You can find the sources for these property editors inbeans/apis/sun/beans/editors.
Beans communicate with other Beans by sending and
receiving event notifications.
To see which eventsSimpleBeancan send, choose
the Edit|Events BeanBox menu item. A list of events,
grouped by the Java interface in which the event method is
declared, will be displayed. Under each interface
group is a list of event methods. These are all
inherited from Canvas.
You will learn more about
properties and
events in
upcoming sections.
Generating Bean Introspection Reports
Introspection is the process of discovering a
Bean's design-time features by one of two
methods:
- Low-level reflection, which uses design
patterns to discover your Bean's features
- By examining an associated
bean information
class that explicitly describes your Bean's features.
You can generate a Bean introspection report
by choosing the Edit|Report menu item. The
report lists Bean events, properties, and methods,
and their characteristics.
By default Bean reports are sent
to thejavainterpreter's standard output, which is the window
where you started the BeanBox. You can redirect the
report to a file by changing the java interpreter
command inbeanbox/run.shorrun.batto:
java sun.beanbox.BeanBoxFrame > beanreport.txt
Writing a Simple Bean
Creating a simple Bean
Compiling and saving the Bean into a Java Archive (JAR) file
Loading the Bean into the ToolBox
Dropping a Bean instance into the BeanBox
Inspecting the Bean's properties, methods, and events
Generating an introspection report
Your Bean will be named SimpleBean. Here are the steps to create it and view it in the BeanBox:
Write the SimpleBean code. Put it in a file named SimpleBean.java, in the directory of your choice. Here's the code:
import java.awt.*;
import java.io.Serializable;
public class SimpleBean extends Canvas
implements Serializable
{
//Constructor sets inherited properties
public SimpleBean(){
setSize(60,40);
setBackground(Color.red);
}
}
SimpleBean extends the java.awt.Canvas component. SimpleBean also implements the java.io.Serializable interface, a requirement for all Beans. SimpleBean sets the background color and component size.
Make sure the CLASSPATH environment variable is set to point to all needed .class (or .jar) files. Here are some URLs that will help you to set CLASSPATH correctly:
The Managing Source and Class Files lesson gives good advice on how and when to set your CLASSPATH.
The JDK Tool Reference Page provides complete CLASSPATH information for both Windows and Solaris platforms.
Compile the Bean: javac SimpleBean.java
This produces the class file SimpleBean.class
Create a manifest file. Use your favorite text editor to create a file, we'll call it manifest.tmp, that contains the following text: Name: SimpleBean.class
Java-Bean: True
Create the JAR file. The JAR file will contain the manifest and the SimpleBean class file: jar cfm SimpleBean.jar manifest.tmp SimpleBean.class
See the Packaging Programs in JAR Files trail, and the JDK JAR file documentation for complete information on JAR files.
Load the JAR file into the ToolBox. Select the FileLoadJar... menu item. This will bring up a file browser. Navigate to the SimpleBean.jar location and select it. SimpleBean will appear at the bottom of the ToolBox. (Note that when the BeanBox is started, all Beans in JAR files in the beans/jars directory are automatically loaded into the ToolBox).
Drop a SimpleBean instance into the BeanBox. Click on the word SimpleBean in the ToolBox. The cursor will change to a crosshair. Move the cursor to a spot within the BeanBox and click. SimpleBean will appear as a painted rectangle with a hatched border. This border means that SimpleBean is selected. The SimpleBean properties will appear in the Properties sheet.
You can resize SimpleBean, because it inherits from Canvas, by dragging a corner. You will see the cursor change to a right angle when over a corner. You can also reposition SimpleBean within the BeanBox by dragging on any non-corner portion of the hatched border. You will see the cursor change to crossed arrows when in position to move the Bean.
SimpleBean Makefiles
Below are two makefiles (Unix and Windows) set up to create SimpleBean.
# gnumake file
CLASSFILES= SimpleBean.class
JARFILE= SimpleBean.jar
all: $(JARFILE)
# Create a JAR file with a suitable manifest.
$(JARFILE): $(CLASSFILES) $(DATAFILES)
echo "Name: SimpleBean.class" >> manifest.tmp
echo "Java-Bean: True" >> manifest.tmp
jar cfm $(JARFILE) manifest.tmp *.class
@/bin/rm manifest.tmp
# Compile the sources
%.class: %.java
export CLASSPATH; CLASSPATH=. ; javac $<
# make clean
clean:
/bin/rm -f *.class
/bin/rm -f $(JARFILE)
Here is the Windows nmake version:
# nmake file
CLASSFILES= simplebean.class
JARFILE= simplebean.jar
all: $(JARFILE)
# Create a JAR file with a suitable manifest.
$(JARFILE): $(CLASSFILES) $(DATAFILES)
jar cfm $(JARFILE) <
Java-Bean: True
<<
.SUFFIXES: .java .class
{sunw\demo\simple}.java{sunw\demo\simple}.class :
set CLASSPATH=.
javac $<
clean:
-del sunw\demo\simple\*.class
-del $(JARFILE)
You can use these makefiles as templates for creating your own Bean makefiles. The example Bean makefiles, in the beans/demo directory, also show you how to use makefiles to build and maintain your Beans.
Inspecting SimpleBean Properties and Events
The Properties sheet displays the selected Bean's properties. With SimpleBean selected, the Properties sheet displays four propeties: foreground, background, font, and name. We declared no properties in SimpleBean (see the Properties section to learn how to declare properties), so these are properties inherited from Canvas. Clicking on each property brings up a property editor. The BeanBox provides default property editors for the primitive types, plus Font and Color types. You can find the sources for these property editors in beans/apis/sun/beans/editors.
Beans communicate with other Beans by sending and receiving event notifications. To see which events SimpleBean can send, choose the EditEvents BeanBox menu item. A list of events, grouped by the Java interface in which the event method is declared, will be displayed. Under each interface group is a list of event methods. These are all inherited from Canvas.
You will learn more about properties and events in upcoming sections.
Generating Bean Introspection Reports
Introspection is the process of discovering a Bean's design-time features by one of two methods:
Low-level reflection, which uses design patterns to discover your Bean's features
By examining an associated bean information class that explicitly describes your Bean's features.
You can generate a Bean introspection report by choosing the EditReport menu item. The report lists Bean events, properties, and methods, and their characteristics.
By default Bean reports are sent to the java interpreter's standard output, which is the window where you started the BeanBox. You can redirect the report to a file by changing the java interpreter command in beanbox/run.sh or run.bat to:
java sun.beanbox.BeanBoxFrame > beanreport.txt
Sunday, October 22, 2006
Cryptography and security on java se 6
The Java Platform, Standard Edition (Java SE) provides application developers with a large set of security APIs, tools, and implementations of commonly used security algorithms, mechanisms, and protocols. These security APIs span a wide range of areas, including cryptography, public key infrastructure, secure communication, authentication, and access control. In addition, the security tools facilitate the ability of users or administrators to securely deploy and manage Java platform applications.
As technology evolves, native platforms undergo many security improvements, for example, cryptographic accelerators, secure key management, more built-in security services, and so on. Leveraging the security offered by the native platform provides several significant benefits to the Java platform: They include but are not limited to the performance boost that cryptographic accelerators provide, a consistent behavior that matches what native applications have when they use the same native library, and the seamless sharing of users' native credentials.
This article will discuss important enhancements on the native security integration using JDK 6. Note: Any API additions or other enhancements to the Java SE platform specification are subject to review and approval by the JSR 270 Expert Group.
After an overview, this article will cover each feature in turn and illustrate the common usage scenarios with examples and sample code. To better understand the examples, you should familiarize yourself with existing security APIs and security provider frameworks. If you are new to Java security, see the Java Security Overview. Additional links are available in the For More Information section at the end of this article.
Note that the enhancements that this article discusses may not be available for all native platforms. The Appendix provides more details, including the first JDK release in which each feature was introduced as well as the native platforms that each feature supports.
Contents
- Access Microsoft CryptoAPI and Its Cryptographic Services
- Access PKCS#11 Cryptographic Services
- Access Native GSS-API
- Import and Export PKCS#12 Keystores
- Conclusion
- Appendix
- For More Information
On the Microsoft (MS) Windows operating system, the MS CryptoAPI (CAPI) defines a standard interface for performing cryptographic operations as well as accessing the user keys and certificates that Windows manages. The SunMSCAPI provider is layered on top of CAPI and helps Java platform applications access CAPI cryptographic services using existing Java technology security and cryptography APIs. This means that Java platform applications can now use the SunMSCAPI provider to do the following:
- Access private keys and certificates stored in CAPI
- Use CAPI's cryptographic algorithm implementations
Table 1 shows the complete list of cryptographic services that the SunMSCAPI provider supports.
Table 1. Cryptographic Services Supported by the SunMSCAPI Provider |
Service Type | Service Name | Service Description | ||
|---|---|---|---|---|
| Generates RSA key pairs needed by other cryptographic services such as | |||
| Creates and validates signatures using various message digest and encryption algorithm as specified in the service name. | |||
| Performs RSA encryption and decryption. | |||
| Provides direct read-write access to MS Window's keystores. The | |||
| Generates random numbers for the random data that other cryptographic services need. | |||
Note that JDK 6 is preconfigured with multiple providers that are listed in order of preference -- with position 1 being most preferred -- as defined in the Java technology security properties file, . More than one provider supports RSA cryptographic services: The SunRsaSign provider offers the RSA KeyPairGenerator and Signature services and the SunJCE provider offers the RSA Cipher service. By default, the SunMSCAPI provider is installed with a preference lower than both to ensure maximum compatibility. A Java platform application that does not indicate which provider to use will get the implementation from the most preferred provider that supports the requested algorithm. You can find more details on provider lookup and management in the section "How Provider Implementations Are Requested and Supplied" of the Java Cryptography Architecture document.
Keys and certificates stored in MS Windows key containers and certificate stores, known as keystores, can be accessed by using the java.security.KeyStore class and the SunMSCAPI provider. The same set of KeyStore APIs are used for accessing MS Windows keystores and other types of keystores, such as JKS or PKCS12. However, because MS Windows keystores do not use passwords and cannot be imported or exported, values for password and input-output stream arguments should be null. By default, the SunMSCAPI provider silently ignores non-null values. In addition, changes are reflected immediately when making modifications to the keystore, such as KeyStore.setKeyEntry(...), KeyStore.deleteEntry(...).
For example, here is one way to sign data by using the RSA private key stored in the MS Windows keystore under the alias myRSA and then to verify the generated signature:
KeyStore ks = KeyStore.getInstance("Windows-MY"); |
Note that keys produced by the SunMSCAPI provider are wrapper objects for the native handles. Thus, they may not be accepted by other providers and may behave somewhat differently than keys produced by pure-Java providers, such as SunJCE. In particular, the RSA private keys generated by the SunMSCAPI provider cannot be serialized and implement the java.security.PrivateKey interface instead of the java.security.interfaces.RSAPrivateKey interface.
If you are writing a Java platform application and want to use the keys and certificates from MS Windows keystores with Java Secure Socket Extension (JSSE), refer to the JSSE Reference Guide for instructions on how to customize the default key and trust stores.
PKCS#11, the Cryptographic Token Interface Standard, defines native programming interfaces to cryptographic tokens such as hardware cryptographic accelerators and smart cards. To help Java platform applications access native PKCS#11 tokens, a new provider was introduced in JDK 5.0 to act as a bridge between the native PKCS#11 tokens and the applications.
This means that Java platform applications can now use existing security and cryptography APIs in the Java platform to take advantage of benefits offered by the underlying PKCS#11 implementations, such as the following:
- Cryptographic smart cards for added security
- Hardware cryptographic accelerators for better performance
- Software implementations for more algorithms or for meeting certification requirements
Note that this provider does not come with its own PKCS#11 implementation and simply uses what it is configured with. On the Solaris 10 operating system, it is preconfigured to use the Solaris Cryptographic Framework. Thus, when the particular machine has cryptographic accelerators built in, all existing Java platform applications benefit automatically without requiring any changes to the configuration or code. To use other PKCS#11 tokens or implementations, the PKCS#11 provider requires a configuration file that supplies information on the PKCS#11 library, which most vendors provide along with their cryptographic devices, as well as a unique name identifier for differentiating this provider from other providers.
The configuration file is a text file that contains mappings of options and their values. Most of the options are for regular PKCS#11 libraries, except for the small number of options specific to Network Security Services (NSS). NSS is an open-source FIPS-140 certified cryptographic implementation used in a variety of products including Mozilla Firefox, AOL Communicator, and Sun Java Enterprise System (JES). NSS cryptographic APIs are based on PKCS#11, but they have special features outside of the PKCS#11 standard and thus require these special configuration options.
NSS is often used in one of the following two modes: as a cryptographic provider for its optimized cryptographic algorithm implementations or as an FIPS-140 compliant cryptographic token.
Assuming that NSS libraries are under the /opt/nss/lib directory and that its key database files -- with the suffix .db -- are under the /opt/nss/fipsdb directory, the sample configurations for using NSS are as follows:
# Use NSS as a pure cryptography provider "SunPKCS11-NSScrypto". |
The configuration of non-NSS PKCS#11 libraries contains at least two options: a string that is concatenated with the prefix SunPKCS11- to produce the name of the PKCS#11 provider as well as the full path to the native PKCS#11 libraries. The sample configuration for naming the PKCS#11 provider to SunPKCS11-Foo using the PKCS#11 library at /opt/foo/lib/libpkcs11.sois as follows:
name = Foo |
You can use other configuration options to further customize the provider. You can also use more than one PKCS#11 token or use multiple slots of the same PKCS#11 token by creating different PKCS#11 providers. Refer to the Java PKCS#11 Reference Guide for the supported options and their use.
Just like other providers, a PKCS#11 provider can be installed dynamically at runtime or statically in the Java technology security properties file, $JAVA_HOME/lib/security/java.security. You can use the following code to programmatically create and install the PKCS#11 provider at runtime:
String configFileName = "/opt/foo/sunpkcs11-Foo.cfg"; |
To statically install the PKCS#11 provider, add the following provider preference entry to the Java technology security properties file:
security.provider.1=sun.security.pkcs11.SunPKCS11 /opt/foo/sunpkcs11-Foo.cfg |
You can find more details on provider lookup and management in the section "How Provider Implementations Are Requested and Supplied" of the Java Cryptography Architecture document.
With this new PKCS#11 provider, Java platform applications can access their PKCS#11 tokens and perform general cryptographic operations such as signature generation and verification through existing security APIs. For certain PKCS#11 features such as dynamically changing smart cards, JDK 6 contains a number of enhancements to better support those features, including added new APIs, updated tools, and so on.
For example, to access private keys on a PKCS#11 token and prompts for a personal identification number (PIN) at runtime, use this code:
// TextCallbackHandler prompts and reads the command line for |
For PKCS#11 tokens that require a PIN even for operations not related to a key, Sun's PKCS#11 provider extends the java.security.AuthProvider class, which allows Java platform applications to supply PINs when needed using the registered callback handler. Note that the callback handler must be able to handle objects of type javax.security.auth.callback.PasswordCallback:
AuthProvider myPKCS11Prov = |
To learn more about using JSSE with PKCS#11 cryptographic services, read the section "JCE and Hardware Acceleration/Smartcard Support" in the JSSE Reference Guide.
Security tools such as keytool, jarsigner, and policytool are updated in JDK 6 to support PKCS#11 keystores as well. For example, to list the PKCS#11 keystore content using keytool, use this code:
// When "SunPKCS11-Foo" provider is statically installed |
Note that keytool will prompt users to enter the PIN information using the command line because the code in the previous sample has not supplied one using the option -storepass. If such behavior is not desired in the application, for example, if the PKCS#11 token has its own dedicated PIN pad, you must specify the option -protected to disable the prompting.
The jarsigner tool supports PKCS#11 keystores with the same set of options as in the keytool example and is quite straightforward. JDK 6 has enhanced the syntax of the keystore entry in the default policy implementation to better support PKCS#11 keystores:
keystore "some_ks_url", "ks_type"[, "ks_provider"]; |
The elements inside the square brackets ([ ]) are optional. Using the PKCS11-Foo provider from the previous examples and assuming its PIN is stored in a file named /opt/foo/sunpkcs11-Foo.passwd, specify the following entry in the policy file:
keystore "NONE", "PKCS11", "SunPKCS11-Foo"; |
Note that the SunPKCS11-Foo provider must be installed statically in order for policytool to validate this keystore entry after it is entered.
The Generic Security Services API (GSS-API) defines a generic security API atop a variety of underlying cryptographic mechanisms including Kerberos version 5. With GSS-API, applications can authenticate a principal, delegate its rights to a peer, and apply security services such as confidentiality and integrity on a per-message basis. Request for Comment (RFC) 2744 and RFC 2853 specify the GSS-API language bindings for C and the Java programming language, respectively.
To help Java platform applications achieve seamless integration with native applications, JDK 6 enhances Java GSS-API to use native GSS-API instead of its own implementation of cryptographic mechanisms when configured to do so. When using the native GSS-API and its underlying native cryptographic mechanisms, the native credentials and settings in users' environment will be picked up automatically. This is different from the default case in which Java GSS-API uses its own implementation of cryptographic mechanisms. When using Kerberos, Java platform applications have to supply Kerberos configuration information using the designated Kerberos system properties in order for Java GSS-API to function. Existing Java GSS documentation covers the default case in great detail, so this section will focus on how to enable or configure Java GSS-API to use native GSS-API. At the end of this section, you will find sample programs that illustrate the point.
Before you enable Java GSS-API to use native GSS-API, it is important to ensure that native GSS-API and its underlying cryptographic mechanism are available and functioning with user settings. For example, you must ensure that native GSS libraries are installed at the appropriate directories with proper configurations, and the same applies to the Kerberos library and configurations. Note that native GSS-API assumes that before an application calls its APIs, it has already obtained and stored the mechanism-specific credentials in a location that the native mechanism implementation is aware of. Thus, when an application uses native GSS-API with Kerberos, it must already have obtained appropriate native credentials, such as Kerberos tickets and keys, using the kinit tool, for example.
To make Java GSS-API use native GSS-API, Java platform applications must explicitly enable this behavior by setting one or more of the following system properties:
sun.security.jgss.native(required): Set this totrueto enable Java GSS-API to use native GSS-API.sun.security.jgss.lib(optional): Set to the full path of the native GSS library. If this is not set, Java GSS-API will look for the native GSS library using the default Java library path. The default name varies by operating system: For the Solaris OS, uselibgss.so; for Linux, uselibgssapi.so.
Note: These two system properties are ignored when applications run on operating systems that do not yet support this feature, for example, MS Windows.
As mentioned previously, native GSS-API requires that the application has obtained these credentials and that they are accessible. Java platform applications can access these native credentials through Java GSS-API and use them for establishing GSS-API security contexts with peers. Note that when a Subject is present, for example,
javax.security.auth.Subject.getSubject(AccessController.getContext()) != null |
Java GSS-API mandates that the credentials be obtained from the private or public credential sets of the current Subject and that the Java GSS-API call must fail if the desired credential cannot be found. Thus, Java platform applications that execute the Java GSS-API calls inside a
Subject.doAs/doAsPrivileged(...) |
call should either populate the Subject's credential sets with appropriate Java GSSCredential objects that encapsulate the native credentials or explicitly set the system property javax.security.auth.useSubjectCredsOnly to false so that Java GSS-API can obtain credentials from other locations, for example, from native credential caches, in addition to the Subject's credential sets.
When delegated to establish a GSS-API security context on behalf of others, Java platform applications can either specify the delegated credential, as returned by GSSContext.getDelegCred(), explicitly in Java GSS-API calls or create a Subject object with this delegated credential and execute the Java GSS-API calls inside the Subject.doAs/doAsPrivileged(...) calls.
Lastly, once the native GSS-API is enabled, Java platform applications that indirectly call Java GSS-API through mechanisms or protocols such as Simple Authentication and Security Layer (SASL) will also use user's native settings and credentials.
Here is some sample code that helps demonstrate how to use Java GSS-API to establish GSS-API security contexts and securely exchange data between three parties: SampleClient contacts FooServer, which in turn contacts FooServer2 on behalf of SampleClient. Note:
The sample code should be invoked with native GSS-API enabled. The Principal names
host@foo.sample.comandhost@foo2.sample.comare placeholders and should be replaced with actual principal names in your Kerberos database.When a security manager is installed, some Java GSS-API calls require that permissions be granted. Check the Java platform documentation of the following classes for more details:
To simplify the example, token exchanges between peers are represented by two pseudo methods:
SEND_TOKEN(byte[])andREAD_TOKEN(). Their actual implementation are application specific and thus not shown here.To reduce code duplication, context establishment code is referred by a pseudo method,
ESTABLISH_CONTEXT(GSSContext), in the code segments forSampleClient,FooServer, andFooServer2.
Following is the implementation using Java GSS-API.
/** |
Following are the code segments for SampleClient, FooServer, and FooServer2:
//================================================================ |
PKCS#12, the Personal Information Exchange Syntax Standard, defines a portable format for storing or transporting personal identity information, including private keys, certificates, and so on. This enables users to import, export, and thus effectively share their personal identity information among applications that support this standard. In particular, user credentials that browsers such as Microsoft IE or Mozilla Firefox generate can be exported in PKCS#12 format -- as files with the .pfx or .p12 suffix -- and then accessed and used by Java platform applications.
The PKCS12 keystore implementation from the SunJSSE provider follows the syntax defined in PKCS#12. All entries are privacy protected, and the whole keystore is then integrity protected using a user-supplied password as defined in PKCS#12 in the Password privacy and Password integrity modes. To ensure maximum interoperability with other vendors' PKCS#12 keystore implementation, the JDK 6 PKCS12 keystore implementation only allows the storing of PrivateKey and its corresponding certificate chain, because other types of entries such as SecretKey or trusted certificate are generally not supported. In addition, although the PKCS12 implementation in JDK 6 is quite flexible and allows the use of different passwords for each private key entry and the integrity of the keystore, the resulting PKCS#12 keystore may not be imported into applications that use only a single password for the keystore and all its key entries.
For example, to import the PKCS12 keystore sample.p12 created by other applications using the password testpass and listing its content, you have two options:
You can invoke the command line
keytool:keytool -list -v -keystore sample.p12 -storepass testpass -storetype PKCS12
You can do this programmatically through the
java.security.KeyStoreAPIs:KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("sample.p12");
char[] passwd = { 't', 'e', 's', 't', 'p', 'a', 's', 's' };
ks.load(fis, passwd);
// Retrieve the PrivateKey and its certificate chain using
// the java.security.KeyStore API.
Enumerationaliases = ks.aliases();
while (aliases.hasMoreElements()) {
String name = aliases.nextElement();
Key privKey = ks.getKey(name, passwd);
java.security.cert.Certificate[] certs =
ks.getCertificateChain(name);
// Print out the desired information using 'name',
// 'privKey', and 'certs'.
...
}
As for creating a PKCS12 keystore and exporting it to other applications, the most straightforward approach would probably be running the command line keytool. For example, to create a PKCS12 keystore sample.p12 that contains a 1024-bit RSA key pair using the password testpass, you could use the following:
keytool -genkeypair -alias myRSAKey -keyalg RSA -keysize 1024 -keystore sample.p12 -storepass testpass -storetype pkcs12 |
Once this command is executed, the file named sample.p12 contains a 1024-bit RSA private key accompanied with a self-signed certificate. For this exercise, users can now import this PKCS12 file into a browser. For real-world use, the self-signed certificate should be replaced with a certificate chain issued by a public certification authority (CA). There are keytool options, for example, -certreq and -import, to generate the certificate request as well as to import the PKCS#7 reply from the CA. The keytool documentation provides a complete list of options and their use. Lastly, you can repeat the previously given keytool command with a different alias to add more PrivateKey entries into the PKCS12 file if needed.
The Java SE platform provides developers a large set of security APIs, tools, and implementations of commonly used security algorithms, mechanisms, and protocols. The ability to leverage the security in the native platform offers several significant benefits: the performance boost that cryptographic accelerators provide, a consistent behavior that matches what native applications have, and the seamless sharing of users' native credentials and settings.
With these enhancements, Java platform applications can now access Microsoft CryptoAPI and its cryptographic services, PKCS#11 cryptographic tokens, and native GSS-API and its mechanisms including Kerberos, as well as sharing credentials such as PrivateKey and Certificates with native applications such as browsers. Furthermore, these enhancements are often implemented as providers that implement a set of services through existing security APIs. Thus, developers of Java platform applications do not have to learn new APIs and can simply reuse most of the existing code.
Table 2 lists information about the native security features this article has discussed and their availability in various operating systems.
Table 2. Native Security Features and Their Availability |
Feature | Starting JDK Release: Available OS | Note |
|---|---|---|
| Access MS CryptoAPI | JDK 6: 32-bit MS Windows | |
| Access PKCS#11 cryptographic services | JDK 5.0: 32-bit and 64-bit Solaris (SPARC, x86), 32-bit Linux, 32-bit MS Windows JDK 6: All except 64-bit MS Windows | Requires PKCS#11 v. 2.0 or later Requires NSS v. 3.11.1 or later (JDK 6) |
| Access Native GSS-API | JDK 6: Solaris, Linux | |
| Import and export PKCS#12 keystores | JDK 1.4: All | Interoperable with MS IE, Netscape, NSS, and OpenSSL |
Friday, September 08, 2006
Eclipse SQL Explorer
Friday, August 04, 2006
Curso gratuito online de diez semanas sobre Ajax
El curso es completamente gratuito y el único requisito de inscripción es enviar un correo electrónico. Los que lo terminen con éxito recibirán un diploma.
Sang Shin es el creador de Javapassion, donde podréis encontrar toneladas de documentación sobre Java creada por él y otrosvoluntarios. Tanto esa documentación como la organización del curso son labores altruistas que realizan en su tiempo libre. A pesar de ello tanto cursos como documentación tiene una gran calidad.
Thursday, July 20, 2006
El modo más simple de lanzar un servidor web J2EE en tu equipo
Mark McLaren, tras el bullicio que se levantó cuando Sun decidió incluir a Derby en su implementación del JDK decidió hace un par de experimentos sobre empotrar bases de datos y servidores en aplicaciones Java. En su weblog nos explica cómo ha hecho sus experimentos y tiene dos demos, una la que incluye a jetty en un Applet, y otra en la que incluye un servidor web simple. Además de ser curioso, es ilustrativo.
Curiosamente, hace poco publicábamos una noticia sobre las posibilidades de Jetty para ser empotrado dentro de una aplicación Java. ¿Cuántos de vosotros habéis empotrado alguna base de datos/servidor/... en una aplicación Java? ¿qué os llevó tomar esa decisión?
Sunday, July 16, 2006
¿Qué guardar en el CVS?
Básicamente hay dos enfoques:
1. primar la compatibilidad: que un programador que entra nuevo al proyecto pueda hacer un checkout y empezar a trabajar en el minuto 0, siempre que su entorno sea como el de los demás.
2. primar el "agnosticismo de plataforma": que cualquier desarrollador pueda trabajar en el proyecto con el ide que más le guste, gestionando él mismo su entorno.
Para gustos colores... yo personalmente prefiero elegir un IDE (el que más gente conozca con soltura, normalemente eclipse), crear un entorno "estandar" y que cualquiera pueda empezar a trabajar con un checkout. Dedicar un día para "configurar el entorno" me parece una pérdida de tiempo.
Java Service Wrapper 3.2.1
Esta nueva versión corrige bugs encontrados en anteriores versiones e incluye nuevas características como el soporte para poner pausa a los servicios en Windows.
http://wrapper.tanukisoftware.org/doc/english/introduction.html
Tuesday, July 04, 2006
Artículo sobre cómo imprimir desde Java
Java Pro Programming: Printing
Learn how to use the print service API
SummaryBy Brett Spell
In this article, an excerpt from Pro Java Programming (Apress, June 2005), Brett Spell explains step-by-step how to locate print services, create a print job, create an implementation of theDocinterface that describes the data you want to print, and initiate printing. (4,500 words; July 25, 2005)
![]()
Printer-friendly version |
Mail this to a friend
| Advertisement | |
|
PrintJob in the java.awt package, but the printing capabilities supported by that class were somewhat crude and unreliable. When Java 1.2 (or "Java 2") was introduced, it included a completely separate mechanism (called the Java 2D printing API) for printing designed around PrinterJob and other classes and interfaces defined in the new java.awt.print package. This rendered the PrintJob-based printing mechanism (also known as AWT printing) largely obsolete, although PrintJob has never been deprecated and, at least of this writing, is still technically a supported class. Additional changes were made in J2SE 1.3 when PrintJob's capabilities expanded to allow the setting of job and page attributes using the appropriately named JobAttributes and PageAttributes classes within the java.awt package. With the release of J2SE 1.3, the printing capabilities were reasonably robust, but some problems still existed aside from the confusion associated with having two completely separate printing facilities. For one thing, both facilities used an implementation of the java.awt.Graphics class for rendering the content to be printed, which meant anything that needed to be printed had to be rendered as a graphical image. In addition, the newer and generally more robust PrinterJob facility provided only limited support for setting attributes associated with the job. Finally, neither facility provided a way to programmatically select the target printer.
The biggest change in Java's printing capabilities to date came with the release of J2SE 1.4, when the Java print service API was introduced. This third implementation of printing support in Java addressed the limitations that were just described using an implementation of the PrintService and DocPrintJob interfaces defined in the javax.print package. Because this new API represents a superset of the functionality defined by the two older printing facilities, it's the one you should normally use and will be the focus of this article.
At a high level, the steps involved in using the Java print service API are straightforward:
- Locate print services (printers), optionally limiting the list of those returned to the ones that support the capabilities your application needs. Print services are represented as instances of
PrintServiceimplementations. - Create a print job by calling the
createPrintJob()method defined in thePrintServiceinterface. The print job is represented by an instance ofDocPrintJob. - Create an implementation of the
Docinterface that describes the data you want to print. You also have the option of creating an instance ofPrintRequestAttributeSetthat describes the printing options you want. - Initiate printing by calling the
print()method defined in theDocPrintJobinterface, specifying theDocyou created in the previous step and thePrintRequestAttributeSetor a null value.
You'll now examine each of these steps and see how to achieve them.
| Note |
|---|
| Within this article, I'll use the terms printer and print service interchangeably because, in most cases, a print service is nothing more than a representation of a physical printer. The more generic print service reflects that the output can theoretically be sent to something other than a printer. For example, a print service might not print the output at all but instead write it to a disk file. In other words, all printers are represented by a print service, but not every print service necessarily corresponds to a physical printer. In practice, though, it's likely you'll almost always send your content to a printer, which is why I'll sometimes use the simpler printer term instead of the more technically accurate print service. |
Locating print services
You locate a printer using one of three static methods defined in the PrintServiceLookup class. The simplest of the three methods is lookupDefaultPrintService(), and, as its name implies, it returns a reference to the service that represents your default printer:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
Although this method is simple and convenient, using it to select which printer to send output to means you're implicitly assuming that the user's default printer will always be able to support the capabilities your application needs in order to be able to print its output correctly. In practice, you'll typically want to select only those printers that are able to handle the type of data you want to print and that support the features your application needs, such as color or two-sided printing. To retrieve the list of all defined printers or to retrieve a list that's limited to printers supporting certain capabilities, you'll want to use one of two other static methods defined in PrintServiceLookup: either lookupPrintServices() or lookupMultiDocPrintServices().
The lookupPrintServices() method accepts two parameters: an instance of DocFlavor and an instance of some implementation of the AttributeSet interface. As you'll see shortly, you can use both of these to limit the list of printers returned by the method, but lookupPrintServices() allows you to specify a null value for either or both of the two parameters. By specifying a null value for both parameters, you're effectively requesting that the method return a PrintService instance for every printer that's available. At this point, you haven't really examined the methods defined in PrintService, but one of them is the getName() method, which returns a String representing the name of the printer. You can display a list of all printers available on your system by compiling and running code like this:
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
for (int i = 0; i < services.length; i++) {
System.out.println(services[i].getName());
}
For example, if you have access to printers named Alpha, Beta, and Gamma that are attached to a server named PrintServer, running the previous code produces this output:
\\PrintServer\Alpha
\\PrintServer\Beta
\\PrintServer\Gamma
Now let's examine the parameters you can pass to the lookupPrintServices() method and see how they allow you to limit the printers returned to those with only certain capabilities.
DocFlavor
The first parameter you can specify on a call to lookupPrintServices() is an instance of the DocFlavor class, which describes the type of data to be printed and how that data is stored. In most cases, it won't be necessary for you to create a new instance of DocFlavor because Java includes many predefined instances, allowing you to simply pass a reference to one of those instances to lookupPrintServices(). However, let's look at the DocFlavor constructor and methods to understand how an instance is used by a print service.
The two arguments required when creating an instance of DocFlavor are both String instances, with one representing a MIME (Multipurpose Internet Mail Extensions) type and the other being the name of a representation class. The MIME type is used by a DocFlavor to describe the type of data to be printed. For example, if you're printing a gif file, you'll need to use a DocFlavor that has a MIME type of image/gif. Similarly, you might use a MIME type of text/plain if you're printing text information or text/html for an HTML document.
Representation class
While the MIME type describes the type of data to be printed, the representation class describes how that data is to be made available to the print service. DocFlavor includes seven static inner classes, with each one corresponding to a representation class and each one corresponding to a different way of encapsulating the data that's to be printed.
Table 1 shows the names of the static inner classes defined within DocFlavor and their corresponding representation classes. Note that aside from SERVICE_FORMATTED (which I'll discuss in detail later), each one is described as being associated with either "binary" or "character" data. In reality, the distinction is somewhat artificial because character data is really just a specialized form of binary data, in this case, referring to binary data that contains only human-readable characters and perhaps some formatting characters such as tabs, carriage returns, and so on. However, the distinction is important because it reflects that character-oriented representation classes aren't appropriate for storing binary data that's to be printed.
For example, you wouldn't store a representation of a gif image in a character array or a String, and you wouldn't make it accessible through a Reader implementation. On the other hand, since "character" data is just a specialized type of binary data, it's entirely appropriate to store text information in a byte array or make it accessible through an InputStream or via a URL.
Table 1. DocFlavor's predefined representation classesInner class name Representation class Data type BYTE_ARRAY [B (byte[])Binary CHAR_ARRAY[C (char[]) Character INPUT_STREAM java.io.InputStreamBinary READERjava.io.Reader Character SERVICE_FORMATTEDjava.awt.print.Pageable or java.awt.print.PrintableOther STRINGjava.lang.StringCharacter URLjava.net.URL Binary
Each of these static inner classes defined within DocFlavor corresponds to a particular representation class, but remember that I said each DocFlavor instance encapsulates both a representation class and a MIME type that identifies the type of data to be printed. To access an instance of DocFlavor that corresponds to both the representation class and the MIME type of the content you want to print, you'll need to reference an inner class within one of the inner classes listed in Table 1. For example, let's suppose you want to print a gif file that's available on the Web through a URL. In this case, the obvious choice for the representation class is java.net.URL, which is associated with the static class named URL that's defined within DocFlavor. If you browse the documentation for that inner class, you'll find that it, in turn, defines a number of static inner classes, each one corresponding to a particular MIME type representing data types commonly supported by printers. Table 2 shows the inner classes defined within DocFlavor.URL and their corresponding MIME types.
Table 2. The DocFlavor.URL inner classesStatic inner classes MIME type AUTOSENSEapplication/octet-stream GIFimage/gif JPEGimage/jpeg PCLPCL application/vnd-hp.PCL PDFapplication/pdf PNGimage/png POSTSCRIPTapplication/postscript TEXT_HTML_HOSTtext/html TEXT_HTML_US_ASCII text/html;charset=us-ascii TEXT_HTML_UTF_16text/html;charset=utf-16 TEXT_HTML_UTF_16BEtext/html;charset=utf-16be TEXT_HTML_UTF_16LEtext/html;charset=utf-16le TEXT_HTML_UTF_8TEXT_HTML_UTF_8 text/html;charset=utf-8 TEXT_PLAIN_HOSTtext/plain TEXT_PLAIN_US_ASCIItext/plain;charset=us-ascii TEXT_PLAIN_UTF_16 text/plain;charset=utf-16 TEXT_PLAIN_UTF_16BEtext/plain;charset=utf-16be TEXT_PLAIN_UTF_16LE text/plain;charset=utf-16le TEXT_PLAIN_UTF_8text/plain;charset=utf-8
Since you'll print a gif image that's available through a URL, you can access an appropriate DocFlavor instance using this code:
DocFlavor flavor = DocFlavor.URL.GIF;
This code creates a reference to the static instance of DocFlavor that has a representation class of java.net.URL and a MIME type of image/gif.
The classes listed in Table 2 are defined within the DocFlavor.URL class, but what about the other six inner classes defined within DocFlavor? Again, I'll defer a discussion of SERVICE_FORMATTED until later, but, as for the classes associated with binary data types, all three (BYTE_ARRAY, INPUT_STREAM, and URL) include inner classes with the names shown in Table 2. So, for example, if you had loaded the gif data into a byte array, you might instead choose to use code like this:
DocFlavor flavor = DocFlavor.BYTE_ARRAY.GIF;
Just as the three DocFlavor inner classes associated with binary data types include their own inner classes, the three associated with character data types include a different set of inner classes, as shown in Table 3.
Table 3. CHAR_ARRAY, READER, and STRING Static inner class MIME type TEXT_HTMLtext/html;charset=utf-16 TEXT_PLAINtext/plain;charset=utf-16
So, for example, if you wanted to print plain text data that's stored in an instance of String, you could use code like the following:
DocFlavor flavor = DocFlavor.STRING.TEXT_PLAIN;
Similarly, if the text data represented an HTML document and you wanted to have the data printed as it would appear within a Web browser, you could use the following:
DocFlavor flavor = DocFlavor.STRING.TEXT_HTML;
Choosing the right printer
Remember that the discussion of DocFlavor began with a desire to make sure the printer you use actually supports the type of data that's to be printed and the delivery mechanism (representation class) you intend to use. This might seem like an unnecessary step, but, in reality, you may be surprised at which document types a given printer supports. For example, the text-oriented types just described might seem as though they'd be the simplest ones to support, so, if your application is printing plain or HTML text, you might be tempted to simply select the first available print service and send the output to that printer. As it turns out, though, many printers don't support the text-based representation classes, and, if you attempt to send output to a printer that doesn't support the DocFlavor you select, an exception will be thrown like the following:
Exception in thread "main" sun.print.PrintJobFlavorException: invalid flavor
at sun.print.Win32PrintJob.print(Win32PrintJob.java:290)
at PrintTest.main(PrintTest.java:11)
Now that you've seen how to obtain a reference to a DocFlavor and I've discussed the importance of selecting a printer that supports the selected flavor, I'll show how you can use it to make sure you use a printer that supports the flavor you need. As I discussed earlier, the lookupPrintServices() allows you to specify a DocFlavor as its first argument, and, if you specify a non-null value, the method will return only the PrintService instances that correspond to printers that support the specified DocFlavor. For example, the following code will retrieve an array that identifies all printers on your system that can print gif images that are referenced via a URL:
DocFlavor flavor = DocFlavor.URL.GIF;
PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, null);
Alternatively, if your application has already retrieved a reference to a PrintService and you want to determine whether it supports a particular flavor, you can call the isDocFlavorSupported() method. In the following code segment, a reference to the default printer is obtained, and an error message will be displayed if it's not able to print a gif image retrieved via a URL:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocFlavor flavor = DocFlavor.URL.GIF;
if (!service.isDocFlavorSupported(flavor)) {
System.err.println("The printer does not support the appropriate DocFlavor");
}
AttributeSet
As you've now seen, a DocFlavor describes the data to be printed and can be used to ensure that a PrintService supports the corresponding type of data. However, your application may also need to select a printer based upon the features that the printer supports. For example, if you're printing a graph that uses different colors to convey information, you might want to see if a given service supports color printing and, if not, either prevent the printer from being used or render a representation of the graph that doesn't rely on colors.
Characteristics such as the ability to print in color, to print on both sides of a page, or to use different orientations (portrait or landscape) are referred to as a printer's attributes, and the javax.print.attribute package contains many classes and interfaces you can use to describe those attributes. One of those interfaces is AttributeSet, which was mentioned earlier as the second parameter that can be specified on a call to lookupPrintServices(). As you might expect, an implementation of AttributeSet represents a collection of attributes, and specifying a non-null value on the call to lookupPrintServices() will result in only print services being returned that support those attributes. In other words, if you specify both a DocFlavor and an AttributeSet on a call to lookupPrintServices(), the method will return only those printers that support both the specified flavor and the appropriate attributes.
Attribute
Given that an AttributeSet is a collection of attributes, the obvious question is, how do you go about specifying the attribute values that should make up that collection? The javax.print.attribute package also includes an interface named Attribute, and, as you'll see shortly, you create the collection of attributes by adding instances of Attribute to an AttributeSet by calling the add() method. Reviewing the documentation for the Attribute interface reveals that a large number of implementations are defined within the javax.print.attribute.standard package, and it's those classes you'll use. Before you see how that's done, it's helpful to review the other interfaces in the javax.print.attribute package along with their implementations.
Attribute roles
So far, I've described attributes as capabilities of a print service, and while that's largely true, it's really something of an oversimplification, at least in terms of how Java supports attributes. For each different attribute, Java associates it with one or more "roles," and the attribute is valid only in the context of the role(s) with which it's assigned. In other words, various places within the Java print service attributes are used, and not every attribute is valid within every context.
To better understand this, consider the OrientationRequested and ColorSupported implementations of Attribute that are defined within the javax.print.attribute.standard package. The OrientationRequested attribute is one you can specify when creating a document to be printed and allows you to specify the orientation (such as portrait or landscape) that should be used when printing the document. In contrast, ColorSupported is an attribute that can be returned when you call the getAttributes() method of the PrintService interface. In other words, OrientationRequested is an attribute you use to pass information to the print service, and ColorSupported is one that the print service uses to provide you with information about the printer's abilities. You can't specify ColorSupported as an attribute when creating a document to be printed because the printer's ability to print in color isn't something your application is able to control.
Interfaces and implementations
When you first look at the interfaces and classes defined in the javax.print.attribute package, it may appear to present a confusing list of choices when it comes to the interfaces and classes defined there. Aside from the Attribute and AttributeSet interfaces and the HashAttributeSet class that implements AttributeSet, the javax.print.attribute package has four sets of subinterfaces and classes, as shown in Table 4 and Figure 1.
Table 4. Interfaces and classes defined within the javax.print.attribute packageAttribute subinterface AttributeSet subinterface AttributeSet subclass DocAttributeDocAttributeSetHashDocAttributeSet PrintJobAttributePrintJobAttributeSetHashPrintJobAttributeSet PrintRequestAttributePrintRequestAttributeSet HashPrintRequestAttributeSet PrintServiceAttributePrintServiceAttribute PrintServiceAttributeSetHashPrintServiceAttributeSet
Figure 1. The class hierarchy of a portion of the javax.print.attribute package. Click on thumbnail to view full-sized image.
|
So why do you need all these various interfaces and implementations, particularly since the more generalized Attribute, AttributeSet, and HashAttributeSet are provided? The answer is that these specializations are defined to ensure that only the appropriate attributes are used within the role(s) where they're valid. For example, I mentioned that one place where you can use attributes is when creating a document that's to be printed and that some attributes such as ColorSupported aren't valid within that context. When creating such a document, you'll use the DocAttributeSet interface (or more specifically, its HashDocAttributeSet implementation), and the implementation will allow you to add only attributes that implement the DocAttribute interface. The four different types of roles are as follows:
- Doc: Specified when creating a document that's to be printed to describe how the document should be printed
- PrintJob: Attributes returned from the print job to describe the state of the job
- PrintRequest: Attributes passed to the print job when a request is made to initiate printing
- PrintService: Returned by a
PrintServiceto describe the capabilities of the service
To see how this works, let's create an instance of a DocAttributeSet and then attempt to set both the OrientationRequested and ColorSupported attributes for that AttributeSet. The HashDocAttributeSet defines a no-argument constructor, so you can create an instance easily as follows:
DocAttributeSet attrs = new HashDocAttributeSet();
Now that you've created the AttributeSet, you can call its add() method and pass to it instances of Attribute implementations. If you examine the documentation for the OrientationRequested class, you'll see it includes references to a number of static OrientationRequest instances, with each one corresponding to a document orientation, such as portrait or landscape. To specify the orientation you want, all you need to do is pass a reference to the appropriate static instance to the add() method as follows:
DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);
The ColorSupported class is slightly different but equally simple to use, and it defines two static instances: one that indicates that color printing is supported and another that indicates it's not supported. You can attempt to add a ColorSupported attribute to the DocAttributeSet with code like this:
DocAttributeSet attrs = new HashDocAttributeSet();
attrs.add(OrientationRequested.PORTRAIT);
attrs.add(ColorSupported.SUPPORTED);
As mentioned earlier, it's not appropriate to specify whether to support color printing because this isn't something an application is allowed to control. In other words, the ColorSupported attribute isn't valid within the context of a set of document attributes, and, as a result, attempting to run the previous code will cause a ClassCastException to be thrown when it attempts to add the ColorSupported attribute.
To understand how this works, remember that each AttributeSet subinterface (in this case, DocAttributeSet) has a corresponding Attribute subinterface (DocAttribute) and an implementation class (HashDocAttributeSet). When an attempt is made to add an attribute, the implementation class tries to cast the Attribute parameter to the corresponding subinterface type, which, in turn, ensures that only attributes appropriate for that context can be added successfully.
In this case, the add() method of HashDocAttributeSet is first called with an instance of OrientationRequested, and it successfully casts that object to a DocAttribute, because, as Figure 2 shows, OrientationRequested implements that interface. In contrast, however, passing an instance of ColorSupported fails because ColorSupported doesn't implement DocAttribute.
Figure 2. The class hierarchy of a portion of the javax.print.attribute package. Click on thumbnail to view full-sized image.
|
As this example illustrates, the four different groups of interfaces and classes shown in Table 4 ensure that only the appropriate attributes are used within the appropriate context. Notice that a great deal of overlap occurs between roles and the various attributes, so many of the attributes are associated with more than one role. For example, many of the attributes implement both PrintJobAttribute and PrintRequestAttribute because many of the attributes that are maintained and provided to you by a print job correspond to attributes you can specify when you request that printing be initiated. You can, for instance, both specify the job name by adding it to a PrintRequestAttributeSet and retrieve the name of the job during printing by retrieving it from a PrintJobAttributeSet. As a result, the JobName attribute class implements both PrintRequestAttribute and PrintJobAttribute.
AttributeSet and HashAttributeSet
You've now seen why the four groups of subclasses exist, but what about the base AttributeSet interface and the HashAttributeSet superclass? AttributeSet/HashAttributeSet is used in situations where you can't assume that only attributes associated with a single role will need to be stored in a collection. Remember that earlier I mentioned that the lookupPrintServices() method allows you to specify an AttributeSet parameter that will limit which print services are returned. On the surface it might appear that it'd be better to require that an instance of PrintServiceAttributeSet be specified, but many of the attributes you might want to specify don't implement PrintServiceAttribute.
Let's assume you want the lookupPrintServices() method to retrieve only services that support both color printing and landscape printing. Those attributes correspond to the ColorSupported and OrientationRequested attributes, respectively, but notice that those two attribute classes don't share a common role: ColorSupported is a PrintServiceAttribute, and OrientationRequested is associated with all three of the other roles (Doc, PrintRequest, and PrintJob), as shown in Figure 2. What this means is that there's no single role-specific AttributeSet interface/class that can contain both a ColorSupported attribute and a Sides attribute.
The way to create an AttributeSet that contains both an OrientationRequested and a ColorSupported instance is to simply use an instance of the generic HashAttributeSet. Unlike its subclasses, it doesn't limit you to adding attributes associated with a particular role, so you can successfully execute the following code:
AttributeSet attrs = new HashAttributeSet();
attrs.add(ColorSupported.SUPPORTED);
attrs.add(OrientationRequested.LANDSCAPE);
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, attrs);
Printer selection via user interface
Up to this point, I've assumed that the printer to be used would be selected programmatically by the application. In practice, however, it's more common to simply display a dialog and allow the user to select which printer to use when printing the output. Fortunately, Java makes it easy to do just that by using the static printDialog() method in the ServiceUI class defined within the javax.print package.
Aside from the location of the dialog to be displayed, the only parameter values that must be specified on the call to printDialog() are these:
- An array of
PrintServiceinstances from which the user can choose. - The default
PrintService. - An instance of
PrintRequestAttributeSet. This is used to populate the dialog that's displayed, and it returns any changes that were made by the user before the dialog was dismissed.
To illustrate how this works, you can use the following simple code segment to display a print dialog:
PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
PrintService selection = ServiceUI.printDialog(
null, 100, 100, services, svc, null, attrs);
When run, the code produces a dialog like the one shown in Figure 3.
Figure 3. The printer dialog
|
As this code illustrates, the value returned from the printDialog() method is an instance of PrintService that identifies which printer the user selected or null if the user canceled the printer dialog. In addition, the PrintRequestAttributeSet is updated to reflect any changes made by the user through the dialog, such as the number of copies to be printed.
By using the printDialog() method, you can allow users to select which printer their output will be sent to, providing the kind of functionality that users have come to expect from professional applications.
Creating a print job
This is the simplest step involved in printing, because once you've obtained a reference to a PrintService, all you need to do is call its createPrintJob() method like so:
PrintService service;
.
.
.
DocPrintJob job = service.createPrintJob();
As indicated in the code, the value returned from createPrintJob() is an instance of DocPrintJob, an object that allows you to control and monitor the status of the printing operation. To initiate printing, you'll call the DocPrintJob object's print() method, but, before you do so, you'll need to define the document to be printed and optionally a PrintRequestAttributeSet. You've already seen how to construct and populate an AttributeSet, so I won't review that step; instead, you'll see how you go about defining the document to be printed.
Defining the document to print
The next step in printing is to define the document that's to be printed, which is done by creating an instance of an implementation of the Doc interface defined in the javax.print package. Each instance of Doc has two mandatory attributes and an optional one:
- An
Objectthat represents the data to be printed - An instance of
DocFlavorthat describes the type of data to print - An optional
DocAttributeSetcontaining attributes to use when printing the document
Reviewing the documentation for the Doc interface reveals that the javax.print package includes an implementation of the interface named SimpleDoc, which has a constructor that takes three arguments that match the three attributes described previously. To see how to construct an instance of SimpleDoc, let's assume you want to print two copies of a gif image that's stored at http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif.
All that's needed to construct a SimpleDoc instance that describes the document to be printed is to create a URL that points to the image, obtain a reference to the appropriate DocFlavor, and pass those two objects to the SimpleDoc constructor as follows:
URL url = new URL(
"http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif");
DocFlavor flavor = DocFlavor.URL.GIF;
SimpleDoc doc = new SimpleDoc(url, flavor, null);
Initiating printing
The final step involved in printing is to call the DocPrintJob's print() method, passing it the Doc object that describes the data to be printed and optionally an instance of PrintRequestAttributeSet. For the sake of simplicity, I'll assume the default printer supports the flavor and attributes you need, in which case you could use the following code to print two copies of the gif file referenced in the previous example:
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
DocPrintJob job = service.createPrintJob();
URL url = new URL(
"http://www.apress.com/ApressCorporate/supplement/1/421/bcm.gif ");
DocFlavor flavor = DocFlavor.URL.GIF;
Doc doc = new SimpleDoc(url, flavor, null);
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(new Copies(2));
job.print(doc, attrs);
Note that, in some cases, printing is performed asynchronously, in which case, the call to print() may return before printing has actually completed


