📄 oct01_ericg.txt
字号:
J 2 M E T E C H T I P S
TIPS, TECHNIQUES, AND SAMPLE CODE
WELCOME to the Java Developer Connection(sm) (JDC)
Java(tm) 2 Platform, Micro Edition (J2ME(tm))
Tech Tips, for October 15, 2001. This issue covers:
* Using Passwords to Protect Your MIDlets
* Adapting Images for MIDP Devices
The J2ME Tech Tips are written by Eric Giguere
(http://www.ericgiguere.com), an engineer at iAnywhere
Solutions, inc. Eric is the author of the book "Java 2 Micro
Edition: Professional Developer's Guide" and co-author of the
upcoming "Mobile Information Device Profile for Java 2 Micro
Edition," both books in John Wiley & Sons' Professional
Developer's Guide series.
You can view this issue of the J2ME Tech Tips on the Web at
http://java.sun.com/jdc/J2METechTips/2001/tt1015.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
USING PASSWORDS TO PROTECT YOUR MIDLETS
Security is always a concern when you are writing an application
that deals with sensitive data. It's especially so on handheld
devices, which are more apt to be lost or stolen than a desktop
computer. A handheld device is likely to hold truly personal
information that you don't want strangers to know, things such as
important phone numbers and addresses. Safeguarding that
information should always be a priority.
MIDP security prevents MIDlets in different MIDlet suites from
interacting with each other or with their data. The MIDP runtime
environment also disallows the dynamic loading of new classes
over the network. Apart from system classes, a MIDlet can only
use classes found in its suite's JAR file. If you want any further
security, for example, protecting sensitive information from
disclosure, you need to program it into the MIDlet itself.
The simplest way for a MIDlet to safeguard sensitive information,
is to prompt the user for a password. You can program this into
the MIDlet so that the MIDlet does this when it starts or the
first time it accesses sensitive data. You can also have the
MIDlet ask the user to confirm certain sensitive operations by
reentering the password.
Prompting the user for a password is easily done using either
the TextBox or the TextField class, both part of the
javax.microedition.lcdui package. The TextBox class displays a
top-level window consisting of a title, an optional label, and
a text entry field. No other user interface components can be
added to a TextBox. So for more flexibility, you can use a
TextField component. A TextField is a text entry field that can
be placed on a Form, that is, a top-level window that can
display multiple user interface components. Both components have
an identical interface, so what's being discussed here applies
to either component.
Before looking at some code, however, let's answer an important
question: what kind of password should you prompt for --
alphanumeric or just numeric? Your first thought might be to
allow for alphanumeric passwords, because that's what is
typically used with desktop computers. The reality, however, is
that numeric-only passwords are the only ones that make sense in
a MIDP context. Remember that a MIDP device might have a numeric
keypad instead of a full keyboard -- think MIDP-enabled cellular
telephone, for example. Entering text on a keypad is done by
assigning multiple characters to individual keys and letting the
user press the same key multiple times to cycle through its set
of characters. This is a tedious and error-prone process that can
frustrate the user. It is also more of a security risk, because
password masking, that is, displaying a masking character instead
of the actual character, must be disabled to allow users to
view which characters they are actually entering. But anyone
looking over the user's shoulder also has a clear view of the
password! Using a numeric password makes for quicker and easier
password input. Think of it more like the personal identification
number you enter at an automated banking machine.
Here's the code for a simple password prompter using the TextBox
class:
// Defines a class that prompts the user
// for a numeric password.
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class PasswordPrompter
extends TextBox
implements CommandListener {
private Display display;
private Displayable next;
private int pin;
// Constructor with a default title
public PasswordPrompter( int pin,
int maxchars,
Display display,
Displayable next ){
this( defaultTitle, pin, maxchars,
display, next );
}
// Constructor with a specific title
public PasswordPrompter( String title,
int pin,
int maxchars,
Display display,
Displayable next ){
super( title, "", maxchars,
TextField.NUMERIC | TextField.PASSWORD );
addCommand( okCommand );
setCommandListener( this );
this.display = display;
this.next = next;
this.pin = pin;
display.setCurrent( this );
}
// Responds to the one-and-only command event
// by checking the entered password against
// the pin. If it's OK, the user is "forwarded"
// to the next displayable, otherwise an alert
// is displayed.
public void commandAction( Command c,
Displayable d ){
String pinStr = getString();
try {
if( Integer.parseInt( pinStr ) == pin ){
display.setCurrent( next );
return;
}
}
catch( NumberFormatException e ){
}
Alert alert = new Alert( "Error!",
"Invalid password",
null,
AlertType.ERROR );
setString( "" );
display.setCurrent( alert, this );
}
private static final Command okCommand =
new Command( "OK", Command.OK, 1 );
private static final String defaultTitle =
"Enter Password";
}
To invoke the prompter, just create an instance of the
PasswordPrompter class, passing it the password to check against
(as an integer), the maximum number of characters to allow for
the password, the Display instance for the MIDlet, and the screen
to display if the correct password is entered. Here is a simple
MIDlet that demonstrates the use of the prompter:
// A MIDlet that demonstrates the use of the
// password prompter.
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
public class PasswordTester extends MIDlet
implements CommandListener {
private Display display;
private Command exitCommand
= new Command( "Exit", Command.EXIT, 1 );
public PasswordTester(){
}
protected void destroyApp( boolean unconditional )
throws MIDletStateChangeException {
exitMIDlet();
}
protected void pauseApp(){
}
protected void startApp()
throws MIDletStateChangeException {
if( display == null ){ // first time called...
initMIDlet();
}
}
private void initMIDlet(){
display = Display.getDisplay( this );
// First time through, ask for the password.
new PasswordPrompter( 1234, 4, display,
new TrivialForm() );
}
public void exitMIDlet(){
notifyDestroyed();
}
public void commandAction(
Command c, Displayable d ){
exitMIDlet();
}
// A trivial UI screen
class TrivialForm extends Form {
TrivialForm(){
super( "MainApp" );
addCommand( exitCommand );
setCommandListener( PasswordTester.this );
}
}
}
In the example above, the password is hardcoded to be "1234."
Of course, you wouldn't do this in a real application. What you
would do is get the password either from a record store
maintained by the application or else from an application
property. An application property is a value stored in either the
MIDlet suite manifest or the suite's application descriptor. To
retrieve an application property just call the MIDlet's
getAppProperty method. For example:
MIDlet midlet = ....;
int password;
try {
String p = midlet.getAppProperty( "Password" );
password = Integer.parseInt( p );
}
catch( NumberFormatException e ){
}
Using an application property allows you to customize a MIDlet
suite for a particular customer. For example, as part of a
servlet-controlled download process, the customer could be asked
to enter an email address. The process could then generate a
random password and a custom version of the MIDlet suite, and
send the password to the customer by email. The customer would
have to enter the password to activate the application, once it's
on the device.
A word of caution in all of this: no security measure is
foolproof, and password protection might not be enough security
to protect every application. Data stored in the application
manifest or descriptor is easily read by anyone with access to
those files. Even data stored in a record store isn't secure. On
a Palm device, for example, it's trivial for an experienced
developer to examine the native Palm database that underlies a
record store. If data is truly important, then it should be
stored in an encrypted format.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ADAPTING IMAGES FOR MIDP DEVICES
The Mobile Information Device Profile (MIDP) supports the display
of bitmapped images, but not with the flexibility of Java(tm) 2
Standard Edition (J2SE(tm)). The MIDP specification only
requires support for a single format, the Portable Network
Graphics (PNG) format. PNG was developed primarily as a
replacement for the older Graphics Interchange Format (GIF), and
has the support of the World Wide Web consortium. (See
http://www.w3.org/Graphics/PNG/ for information about PNG.)
Modern web browsers and image editors can load and save images
in PNG format.
Still, many images on the World Wide Web are in GIF or JPEG (a
"lossy" image format better suited for photographs) format, not
PNG. This is a problem for MIDP applications that need to fetch
and display web images. While it is certainly possible to add
the necessary Java code to a MIDlet to perform GIF-to-PNG and
JPEG-to-PNG conversions, it makes more sense to do the
conversion on a server instead of the client. Not only does this
make your application smaller, it is likely to be much faster.
Because you're already fetching images across the network,
fetching the image through a web server extension you've written
is not going to be much slower.
Even images already in PNG format can benefit from being
funneled through a server, because the server can adapt the
image to fit the device's characteristics. For example, the width
and height of the display varies from device to device, so the
server could scale the image appropriately. The server can also
change the bit-depth of the image based on the number of colors
the device supports. One approach, then, is to not package an
application's images with the application but to download them
dynamically the first time the application is run, storing
the customized images locally using the MIDP's Record Management
System (RMS).
In effect, what you need is a "web service" (in a generic sense)
that can dynamically fetch, convert and adapt an image for a
particular device. An obvious way to do this is to write the
service as a Java servlet, which the MIDP application can then
access using HTTP. The basics of how to do this were covered in
a previous J2ME Tech Tip, "Client-Server Communication Over HTTP
Using MIDlets and Servlets", which you can read at
http://java.sun.com/jdc/J2METechTips/2001/tt0820.html#tip2.
This Tech Tips builds on those concepts.
The first thing you need to do is locate some image conversion
code. There are many free and commercial packages available. But
for our purposes let's use Sun's Jimi (Java Image Management
Interface) software development kit, available for download from
http://java.sun.com/products/jimi. The Jimi SDK enhances the
basic image support found in J2SE by providing encoders and
decoders for various image formats. As a bonus, it runs under
Java 1.1.x as well as the Java 2 Platform. Note that a new set of
imaging I/O extensions for the Java 2 Platform, developed under
the Java Community Process, is available starting with J2SE 1.4,
but given that J2SE 1.4 is currently in Beta, and that many
servlet containers are still running with J2SE 1.2 or even
Java 1.1.8, Jimi is a reasonable choice to start with. Download
the Jimi SDK and place the JimiProClasses.zip file in your
servlet container's classpath so that the servlet can use the
Jimi classes.
With the Jimi classes, the servlet is almost trivial to write,
but it's too long to include here. You can download the source
for the servlet from the following URL:
http://www.ericgiguere.com/techtips/ImageAdapter.zip.
Compile the code and install it on your web server. As a test,
you should be able to use a standard web browser to invoke the
servlet. The web browser will display an image of Duke, the
official Java mascot, fetched from the Sun Web site.
Note that the servlet can handle any image supported by Jimi,
including the JPEG, GIF and PNG formats.
Once the servlet is running, it's time to write a MIDlet that
uses it to fetch images. Here is the code for the MIDlet:
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -