![]() ![]() ![]() |
|
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
![]() |
||||||||||||||||||
|
||||||||||||||||||
Tech Tips archive
J2ME Tech TipsMay 29, 2001WELCOME to the Java Developer ConnectionSM (JDC) JavaTM 2 Platform, Micro Edition (J2METM) Tech Tips, for May 29, 2001. This issue covers:
The J2ME Tech Tips are written by Eric Giguere (http://www.ericgiguere.com), an engineer at iAnywhere Solutions, inc, and author of the book "Java 2 Micro Edition: Professional Developer's Guide." THE CONNECTED DEVICE CONFIGURATION AND THE FOUNDATION PROFILEMuch of what's been written about Java 2 Platform, Micro Edition (J2ME) talks about the Connected Limited Device Configuration, or CLDC for short. The CLDC defines an extremely small subset of the Java Platform for very limited devices -- devices, like cellular telephones, that do not have enough memory or enough processing power to handle a full-blown Java implementation. There is, however, another configuration available that is midway between the CLDC and a full Java 2 Platform, Standard Edition (J2SETM) implementation: the Connected Device Profile, or CDC for short. The CDC defines a much less radical subset of J2SE. Unlike the CLDC, which is aimed at devices with less (often much less) than 512K bytes of total available memory, the CDC requires devices to have at least 2 megabytes of total memory available for Java. This can be a combination of read-only and read-write memory. The Java virtual machine* (VM) and core class libraries are likely to be stored in read-only or flash memory. The CDC really targets the next generation of wireless, handheld consumer devices. These devices pack large amounts of memory and faster processors into the same or smaller form factors than today's most prevalent devices. The CDC can also be used for larger consumer devices such as set-top boxes that deliver content through televisions, that is, devices that don't need a general purpose Java implementation such as J2SE. The CDC is also a superset of the CLDC. All the classes of the CLDC, including the new javax.microedition.io classes defined by the Generic Connection Framework, are part of the CDC. This means that CLDC-based applications can run unchanged in a CDC-based environment. That's true provided that any required CLDC-based profiles the applications use (such as the Mobile Information Device Profile) are available on the device. Perhaps the biggest difference between the CDC and the CLDC is that the CDC includes the CVM virtual machine, a full J2SE-compliant virtual machine. The CLDC made changes to the virtual machine specification. It disallowed all floating-point operations, changed the way classes are verified, and did not support low-level standards such as the Java Native Interface (JNI) or the Java Virtual Machine Debug Interface (JVMDI). By comparison, all of the features of a full J2SE virtual machine must be supported by the CVM virtual machine. The J2SE VM itself is not a requirement of the CDC --- all the CDC requires is a VM that implements the full set of VM features. Vendors can license it and adapt it for their devices quickly. The CVM virtual machine can even be used with real-time operating systems.
The CDC includes classes from the
Just as there are profiles for the CLDC, there are also profiles being developed for the CDC. The first profile, the Foundation Profile, was released at the same time as the CDC. Generally, the two are meant to go hand-in-hand. The Foundation Profile extends the CDC by adding most of the missing J2SE core libraries, except for those related to creating user interfaces. These additions include socket classes, the complete internationalization and localization classes, and the full set of classes in the As you might guess, the Foundation Profile is meant to serve as a foundation for other profiles. The PersonalJavaTM API is being redefined as a Personal Profile that extends the Foundation Profile. An RMI Profile, in the final stages of development, adds RMI support to the Foundation Profile. Other profiles are sure to be developed. Reference implementations of the CDC and the Foundation Profile are available for Linux and VxWorks platforms under the terms and conditions of the Sun Community Source License (SCSL) agreement. You can download them by following the links from the main CDC page.
Here's a CDC-Foundation Profile application that illustrates some of the things described in this tip. In particular, the application uses CDC Here's the source code for PeerFinder: import java.io.*; import java.net.Socket; import java.net.InetAddress; import java.util.Vector; public class PeerFinder { static final int PORT_NUM = 4321; static final int DATA_BUFFER_MAX = 256; static final String PING_MSG = new String("ping"); static final String PONG_MSG = new String("pong"); static String command = System.getProperty("command"); static String portprop = System.getProperty("port"); static String hostname = System.getProperty("hostname"); static Socket socket; static int port = PORT_NUM; static BufferedOutputStream outputStream; static BufferedInputStream inputStream; public static void main(String args[]) { byte[] dataBuffer = new byte[DATA_BUFFER_MAX]; String dataString = null; // Set default port if not set by user if ((portprop != null) && !portprop.equals("")) { port = Integer.parseInt(portprop); } // Set default command if not set by user if ((command == null) || (command.equals(""))) { command = PING_MSG; } // Set default security manager try { if (System.getSecurityManager()==null) { System.setSecurityManager(new SecurityManager()); } } catch (Exception e) { e.printStackTrace(); } // Open a socket to the given peer listener host port try { if ((hostname != null) && hostname.equals("")) { System.out.println("Opening to local host port # "+port); socket = new Socket( InetAddress.getLocalHost(), port); } else { System.out.println("Opening peer listener host "+ hostname+ " port # "+port); socket = new Socket(hostname, port); } } catch (Exception e) { e.printStackTrace(); } try { // Get the input and output streams of socket to peer outputStream = new BufferedOutputStream( socket.getOutputStream()); inputStream = new BufferedInputStream( socket.getInputStream() ); // Check to send a ping if (command.equals(PING_MSG)) { sendPing(); } // Listen for peer listener reply System.out.println("Listening for return message..."); // Reading from peer listener while (inputStream.read(dataBuffer, 0, DATA_BUFFER_MAX) != -1) { dataString = new String(dataBuffer); // Print received message System.out.println("Received peer listener reply: "+ dataString); // Check if pong was received if (dataString.indexOf(PONG_MSG) != -1) { break; } } // Cleanup and close inputStream.close(); outputStream.close(); socket.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } static void sendPing() { byte[] dataBuffer = PING_MSG.getBytes(); try { outputStream.write(dataBuffer, 0, dataBuffer.length); outputStream.flush(); System.out.println("Sent: "+PING_MSG); } catch (Exception e) { e.printStackTrace(); } } }
Here's the source code for import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class PeerListener { static final int PORT_NUM = 4321; static final int DATA_BUFFER_MAX = 256; static final String PING_MSG = new String("ping"); static final String PONG_MSG = new String("pong"); static String command = System.getProperty("command"); static String portprop = System.getProperty("port"); static ServerSocket peerSocket; static Socket socket; static int port = PORT_NUM; static BufferedOutputStream outputStream; static BufferedInputStream inputStream; public static void main(String args[]) { byte[] dataBuffer = new byte[DATA_BUFFER_MAX]; String dataString = null; // Set default property if not set by user if ((portprop != null) && !portprop.equals("")) { port = Integer.parseInt(portprop); } // Set default security manager try { if (System.getSecurityManager()==null) { System.setSecurityManager(new SecurityManager()); } } catch (Exception e) { e.printStackTrace(); } // Open a peer socket to the given port try { peerSocket = new ServerSocket(port); } catch (Exception e) { e.printStackTrace(); } // Start listening for peers System.out.println("Listening for peer finder connection..."); while (true) { try { // Block here for peer finder requests to connect socket = peerSocket.accept(); // Get the input and output streams from peer socket outputStream = new BufferedOutputStream(socket.getOutputStream()); inputStream = new BufferedInputStream(socket.getInputStream() ); // Read loop while (inputStream.read(dataBuffer, 0, DATA_BUFFER_MAX-1) != -1) { dataString = new String(dataBuffer); System.out.println("Received: "+ dataString+""); // Check if this is a ping request message if ((dataString.indexOf(PING_MSG)) != -1) { // Send pong message to the peer sendPong(); } else { // Don't know what type of message this was System.out.println(" Unknown message"); } } inputStream.close(); outputStream.close(); socket.close(); } catch (IOException ioe) { ioe.printStackTrace(); break; } } } static void sendPong() { byte[] dataBuffer = PONG_MSG.getBytes(); try { outputStream.write(dataBuffer, 0, dataBuffer.length); outputStream.flush(); System.out.println("Sent: "+ PONG_MSG); } catch (Exception e) { e.printStackTrace(); } } }
You will also need a policy file and a security properties file to run the application. Use the following policy file ( grant { permission java.net.SocketPermission "*:1024-65535", "connect,accept,listen,resolve"; }; // Standard extensions get all permissions by default grant codeBase "file:${java.home}/lib/ext/*" { permission java.security.AllPermission; }; // default permissions granted to all domains grant { // Allows any thread to stop itself using the // java.lang.Thread.stop() method that takes no argument. // Note that this permission is granted by default only // to remain backwards compatible. // It is strongly recommended that you either remove this // permission from this policy file or further restrict // it to code sources that you specify, because // Thread.stop() is potentially unsafe. // See "http://java.sun.com/notes" for more information. permission java.lang.RuntimePermission "stopThread"; // allows anyone to listen on un-privileged ports permission java.net.SocketPermission "localhost:1024-", "listen"; // "standard" properies that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission " java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
Use the following master security properties file ( # # This is the "master security properties file". # # In this file, various security properties are set for use by # java.security classes. This is where users can statically # register Cryptography Package Providers ("providers" for short). # The term "provider" refers to a package or set of packages that # supply a concrete implementation of a subset of the cryptography # aspects of the Java Security API. A provider may, for example, # implement one or more digital signature algorithms or message # digest algorithms. # # Each provider must implement a subclass of the Provider class. # To register a provider in this master security properties file, # specify the Provider subclass name and priority in the format # # security.provider.<n>=<className> # # This declares a provider, and specifies its preference # order n. The preference order is the order in which providers are # searched for requested algorithms (when no specific provider is # requested). The order is 1-based; 1 is the most preferred, # followed by 2, and so on. # # <className> must specify the subclass of the Provider class whose # constructor sets the values of various properties that are # required for the Java Security API to look up the algorithms or # other facilities implemented by the provider. # # There must be at least one provider specification in # java.security. There is a default provider that comes standard # with the JDK. It is called the "SUN" provider, and its Provider # subclass named Sun appears in the sun.security.provider package. # Thus, the "SUN" provider is registered via the following: # # security.provider.1=sun.security.provider.Sun # # (The number 1 is used for the default provider.) # # Note: Statically registered Provider subclasses are # instantiated when the system is initialized. Providers can be # dynamically registered instead by calls to either the addProvider # or insertProviderAt method in the Security class. # # List of providers and their preference orders (see above): # security.provider.1=sun.security.provider.Sun security.provider.2=com.sun.rsajca.Provider # # Class to instantiate as the system Policy. This is the name of # the class that will be used as the Policy object. # policy.provider=sun.security.provider.PolicyFile # The default is to have a single system-wide policy file, # and a policy file in the user's home directory. policy.url.1=file:${java.home}/lib/security/java.policy policy.url.2=file:${user.home}/.java.policy # whether or not we expand properties in the policy file # if this is set to false, properties (${...}) will not be expanded # in policy files. policy.expandProperties=true # whether or not we allow an extra policy to be passed on the # command line with -Djava.security.policy=somefile. Comment out # this line to disable this feature. policy.allowSystemProperty=true # whether or not we look into the IdentityScope for trusted # Identities when encountering a 1.1 signed JAR file. If the # identity is found and is trusted, we grant it AllPermission. policy.ignoreIdentityScope=false # # Default keystore type. # keystore.type=jks # # Class to instantiate as the system scope: # system.scope=sun.security.provider.IdentityDatabase # # List of comma-separated packages that start with or equal this # string will cause a security exception to be thrown when # passed to checkPackageAccess unless the corresponding # RuntimePermission ("accessClassInPackage."+package) has # been granted. package.access=sun. # # List of comma-separated packages that start with or equal this # string will cause a security exception to be thrown when # passed to checkPackageDefinition unless the corresponding # RuntimePermission ("defineClassInPackage."+package) has # been granted. # # by default, no packages are restricted for definition, and none # of the class loaders supplied with the JDK call # checkPackageDefinition. # #package.definition= To build the application:
In another window, run cd <cdcfoundation_dir>/build/linux/bin cvm -Djava.class.path=<your_test_dir> PeerFinder You'll see the following lines displayed in the window: Sent: ping Listening for return message... Received peer listener reply: pong Some things to note about this example:
HANDLING MULTIPLE SIMULTANEOUS MIDP ALERTS
The April 16, 2001 issue of the J2ME Tech Tips introduced you to timers and alerts. If you write a multithreaded MIDP application -- whether you spawn the threads yourself or use timers to schedule background tasks -- it's easy to trigger two or more alerts simultaneously. In other words, while one alert is being displayed, a second alert is triggered. Dealing with this situation is not as simple as you might think. Alerts are displayed using the two-argument form of the Display display = ...; Alert alert = ...; Displayable next = ...; display.setCurrent( alert, next ); The first argument is the alert to display and the second is the screen object to display after the alert is dismissed or times out. The second argument is referred to as the "alert destination."
The problem with calling Display display = ...; Alert alert = ...; Displayable next = ...; Displayable current = display.getCurrent(); if( current instanceof Alert ){ display.setCurrent( (Alert)current, alert ); } else { display.setCurrent( alert, next ); } This doesn't work, however, because only one alert can be active at any time. The final destination, that is, the destination of the last alert, is also lost with this scheme. What you really need is a way to keep a list of pending alerts and display each alert in turn. This would be simple to do if the alert had a dismissal event you could trap. Unfortunately, there's no direct way to know when an alert is dismissed.
There is an indirect route you can take, however. A public class MyCanvas extends Canvas { protected void paint( Graphics g ){ // do the painting here } protected void showNotify(){ // canvas is being displayed, start // any timers, etc. } protected void hideNotify(){ // canvas is being hidden, stop any // timers, etc. } }
You can take advantage of this capability to solve the alert dilemma. Whenever you display an alert, make a
Armed with the overall design, you can now build the package com.j2medeveloper.util; import java.util.*; import javax.microedition.lcdui.*; public class AlertRouter extends Canvas { private Display display; private Vector pending = new Vector(); private Displayable destination; private Alert current; public AlertRouter( Display display ){ this.display = display; } protected void paint( Graphics g ){ // no painting } protected synchronized void showNotify() { if( pending.size() > 0 ){ current = (Alert) pending.elementAt( 0 ); pending.removeElementAt( 0 ); display.setCurrent( current, this ); } else { current = null; display.setCurrent( destination ); } } public void showAlert( Alert alert ) { showAlert( alert, null ); } public synchronized void showAlert( Alert alert, Displayable next ){ if( next != null ){ destination = next; } else if( destination == null ){ destination = display.getCurrent(); if( destination == null ){ destination = this; } } pending.addElement( alert ); if( current == null ){ display.setCurrent( this ); } } }
To display alerts, create an instance of import com.j2medeveloper.util.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class MyMIDlet extends MIDlet { private Display display; private AlertRouter router; public MyMIDlet(){ display = Display.getDisplay( this ); router = new AlertRouter( display ); } protected void startApp(){ Alert alert = new Alert( "Started", "App started", null, null ); router.showAlert( alert ); } // other methods omitted.... }
Whenever Here's a more complicated MIDlet that tests the alert router by using several timers to create alerts at different time intervals: import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import java.util.*; import com.j2medeveloper.util.*; public class MultiAlert extends MIDlet { Display display; Command exitCommand = new Command( "Exit", Command.EXIT, 1 ); Timer timer1 = new Timer(); Timer timer2 = new Timer(); Timer timer3 = new Timer(); MainForm form = new MainForm(); AlertRouter router; public MultiAlert() { display = Display.getDisplay( this ); router = new AlertRouter( display ); timer1.schedule( new AlertTrigger( "Alert 1", "This is alert #1" ), 5000, 10000 ); timer2.schedule( new AlertTrigger( "Alert 2", "This is alert #2" ), 5000, 7000 ); timer3.schedule( new AlertTrigger( "Alert 3", "This is alert #3" ), 5000, 9000 ); } protected void destroyApp( boolean unconditional ) { timer1.cancel(); timer2.cancel(); timer3.cancel(); } protected void startApp() { display.setCurrent( form ); } protected void pauseApp() { } public void exit(){ destroyApp( true ); notifyDestroyed(); } class AlertTrigger extends TimerTask { public AlertTrigger( String title, String message ) { this.title = title; this.message = message; } public void run(){ Alert alert = new Alert( title, message, null, null ); alert.setTimeout( Alert.FOREVER ); router.showAlert( alert ); } private String title; private String message; } class MainForm extends Form implements CommandListener { public MainForm(){ super( "MultiAlert Demo" ); addCommand( exitCommand ); setCommandListener( this ); } public void commandAction( Command c, Displayable d ){ exit(); } } } Run this MIDlet and wait a few seconds for the first alert to appear, then wait a few more seconds before dismissing it. You'll see another alert appear immediately after the first. Dismiss this second alert and any other alerts. Eventually, you will be brought back to the main screen, where the whole process starts again. Note Sun respects your online time and privacy. The Java Developer Connection mailing lists are used for internal Sun MicrosystemsTM purposes only. You have received this email because you elected to subscribe. To unsubscribe, go to the Subscriptions page, uncheck the appropriate checkbox, and click the Update button. As of May 22, 2001, Sun Microsystems updated its Privacy Policy to give you a better understanding of Sun's Privacy Policy and Practice. If you have any questions, contact privacy@sun.com. Subscribe To subscribe to a JDC newsletter mailing list, go to the Subscriptions page, choose the newsletters you want to subscribe to, and click Update. Feedback Comments? Send your feedback on the J2ME Tech Tips to: Archives You'll find the J2ME Tech Tips archives at: http://java.sun.com/jdc/J2METechTips/index.html Copyright
Copyright 2001 Sun Microsystems, Inc. All rights reserved. This Document is protected by copyright. For more information, see: http://java.sun.com/jdc/copyright.html - LINKS TO NON-SUN SITES The J2ME Tech Tips may provide, or third parties may provide, links to other Internet sites or resources. Because Sun has no control over such sites and resources, You acknowledge and agree that Sun is not responsible for the availability of such external sites or resources, and does not endorse and is not responsible or liable for any Content, advertising, products, or other materials on or available from such sites or resources. Sun will not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with use of or reliance on any such Content, goods or services available on or through any such site or resource. J2ME Tech Tips May 29, 2001 Sun, Sun Microsystems, Java, Java Developer Connection, J2ME, J2SE, and PersonalJava are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. |