Java Language Benefits


For most readers of this book, the benefits of the Java programming language are well-known. Java is an extremely popular and mature language that has a proven track record.

Java is an object-oriented language. Java was designed to be object-oriented from the start. Java is also simple. It has taken many of the benefits of other object-based languages like C++, but removed the complexity. Most programmers find learning Java relatively easy compared with other programming languages in general.

Java provides a large collection of classes that are packaged and segmented into manageable "knowledge chunks." For instance, there is a package that deals with networking, and a package that deals specifically with I/O, and packages used to build visual components. You can specialize or focus your talent and energy on specific aspects of programming. The power of prebuilt frameworks delivered in packages truly accelerates the development and learning process. This is especially important in P2P, which is changing and adapting to many different environments. By its nature, P2P implies many active participants with many different motivations. As a result, it is very dynamic and will require programming languages and tools that can keep pace with its growth and development.

Java is an interpreted language, which is the direction most multipurpose languages will take in the future. This is because the power of interpretation and the benefits gained by eliminating the link-phase in most other languages far outweigh the additional processing time required for interpretation. The virtual machine that performs the runtime interpretation also abstracts the differences between system hardware and operating systems so that Java becomes platform-neutral. You can run your P2P application on Windows, Linux, Sun, Unix, you name it anywhere there is a Java virtual machine defined. Of course, realize that there are areas where performance and squeezing out every millisecond of computing cycles is important. But this is the exception, not the rule. The computing power on your desk today far outweighs what most users even require.

Platform Independence

Java is compiled into bytecodes. The Java virtual machine (JVM) interprets the bytecode before execution on a specific operating system. So, unlike most applications that must be ported to each operating system, Java only requires the JVM to be available on a specific platform. The application can run anywhere. You have already seen how P2P does not try to differentiate between client and server roles. This is known as functional equivalence, and it is an important aspect of P2P architecture. Using Java now provides platform independence coupled with functional equivalence. This is a powerful combination in developing applications that are both portable and dynamic.

The Distributed Nature of Java

The interpretive nature of Java allows classes to be dynamically loaded into the JVM at runtime. Classes can be obtained locally or downloaded from the Internet, providing developers with a single component model that makes programming distributed applications simple. You can easily obtain information about the classes that are executing at runtime through reflection. Reflection allows you to look inside the class definition and determine the fields and methods of the class definition. You can exploit the capabilities of a class without having full knowledge of the class composition using reflection. This capability, coupled with the distributed nature of the language, makes a powerful combination.

The distributed nature of the language is further exemplified by the extensive support of networking in the java.net package. Internet capability was not an afterthought of the Java language; rather it is fundamental to the organization and class definitions of the Java packages. For example, the URL class provides an abstraction of a uniform resource locator. By constructing a URL class, you can easily open a connection to an Internet-accessible resource and download and display the contents. It also provides you with easy access to the HTTP protocol. This capability, coupled with reflection, provides a distributed and dynamic programming environment for P2P applications, while maintaining simplicity.

Remote Method Invocation

Remote Method Invocation (RMI) is a feature of the core Java API. RMI enables method invocation on objects running in separate virtual machines, and even on virtual machines that happen to be on another server in another network. RMI is yet another feature that makes Java suitable for building distributed applications. RMI uses Java serialization to transport objects across a network by writing byte streams that include the necessary information to materialize/unmarshal an object in a remote VM. Not only is the object's data transported, but also the behavior the object implements. This provides a powerful platform for mobile agents that move and migrate across machine and network boundaries. RMI can be used to provide the underpinnings of P2P mobile agents. RMI will be discussed in more detail in Chapter 14, "Jini and JavaSpaces."

Multithread Support

Java provides built-in language support for threads. Threads allow multiple lightweight processes to execute in parallel. At least they appear to be executing in parallel, even on single-processor machines. For example, many P2P programs have the capability to download files while simultaneously playing audio or video clips. The application must be written to support multiple paths of execution simultaneously. Multithreaded applications provide this type of support. Java provides an easy-to-use API that supports threads because it is built into the Java language.

Building applications that are more robust and resilient against network failures requires multithreaded programming. P2P applications often use unstable networks to provide their core functions. Java provides thread support as the alternative to nonblocking, a synchronous I/O, which is used heavily in networking applications written in other languages, such C and C++. These languages must use low-level notification and complex interrupt constructs to provide a similar functionality to what is easily written in Java.

Language Reliability and Maturity

Java has qualities inherent in robust object-oriented languages.

Java is a strongly typed language. Strong typing provides extensive checking for type mismatches that can occur during the development (compilation) and runtime phases of program execution.

Java provides extensive exception handling and requires exceptions to be handled in application code using try-catch blocks. try-catch blocks and exceptions make it easier for programmers to structure code to recover from errors. This is especially important in P2P applications that rely heavily on the network and I/O operations. These operations are especially vulnerable to exceptions, and therefore require more sophisticated error-handling procedures.

Java supports the use of abstract classes and interface definitions. Abstract classes enable you to defer method implementations while still defining method signatures. This is a common idiom in object-oriented programming. It is one of the techniques used to enable polymorphism.

Java also supports interfaces. Interfaces enable you to extend the notion of abstract classes. All the methods defined in an interface are fundamentally abstract. In other words, a concrete class must implement the interface to provide the functionality defined by the methods.

Interface definition enables you to define the methods and method signatures of a class without providing the concrete implementation. Two P2P applications can be written to the same Java interface, and yet have vastly different implementations. One implementation might be focused on speed of execution, while another might provide more reliability. Java interfaces provide an alternative to defining standard interfaces using data-oriented approaches, such as with XML or EDI.

Security

Because Java has been designed to support distributed applications from the ground up, security is an important part of the platform. As you learned earlier, Java makes it possible to load and run classes obtained from network sources or to invoke methods remotely. Naturally, Java protects systems from potentially executing untrusted code, or permitting someone from using resources without permission. Java provides sophisticated and flexible protection of information, systems, and services. P2P applications using Java will benefit from the built-in protection that it offers.

The Java Virtual Machine

It is difficult to understand how Java security works without having a high-level understanding of the Java virtual machine.

From a security perspective, the JVM provides the sandbox that ensures code runs in a protected space and has limited access to system resources. The key components in the JVM security framework are as follows:

  • Class loader

  • Class file verifier

  • Security manager

The Class Loader

The class loader is used by the JVM to locate and load into memory the classes necessary to execute a Java program. Not all class files that are needed come from the local filesystem. For instance, RMI loads classes on demand over the network.

Classes can be divided into two categories: trusted and untrusted, as seen in Figure 2.1.

Figure 2.1. Security in the Java virtual machine.

graphics/02fig01.gif

Trusted classes are those classes that are assumed to be safe. In Java 2, these were restricted to only the Java Runtime Environment (JRE) classes. These are the classes that are found in the boot classpath.

Untrusted classes are all classes that fall outside the boot classpath. This includes the files that are downloaded over the network. The class file verifier verifies these files.

The Class File Verifier

To ensure that a file meets certain criteria, the class loader invokes the class file verifier. The class file verifier performs a series of checks:

  • File integrity check Ensures that a file has the appropriate signature and length.

  • Class integrity check Ensures that all classes, methods, and fields have legal names and signatures.

  • Bytecode integrity check The runtime behavior of the bytecodes is examined and verified.

  • Runtime integrity check Extends the bytecode integrity check to look for code that is actually executed. This is to ensure that code that is never executed does not have to go through the entire verification process unnecessarily.

Other programming languages do not have this level of security built in. This is especially important for P2P applications, because they run in trusted and untrusted environments. One of the strengths of P2P is the ease with which you can discover services or resources in environments that you have little knowledge of. Despite this strength, adequate security must be in place to counter security threats. If the security is built into the language, the development and potential errors that can be introduced in the security architecture are minimized.

The Security Manager

The security manager is responsible for the runtime restrictions that are imposed on code after the code has gone through the verification process. The security manager enforces restrictions based on security policy statements.

The security manager enables an application to determine what operation is being attempted, and whether it is enabled in the current security context. The application can then allow or disallow the operation, depending on whether an application has permission to access a controlled resource.

Permissions fall into these categories:

  • File An application can access the file system to read, write, or delete files or directories, or to execute a file.

  • Socket An application can make or accept a connection using sockets.

  • Net An application can specify an authenticator to handle HTTP authentication, obtain a password from an authenticator, or change the default stream handler for a class that manages URL.

  • Security An application can get security policy information or set security policies, manage security providers, or manage identities.

  • Runtime An application can manage runtime elements, such as replacing the default security manager, create class loaders, or manage threads.

  • Property An application can set or get properties available through the Java virtual machine.

  • Abstract Windowing Toolkit (AWT) An application can access the system clipboard or listen to events from the AWT graphical subsystem.

  • Reflection An application can use reflection to access method or fields within a class.

  • Serializable An application can extend the classes used to serialize or deserialize classes, or perform substitutions of serialized objects.

As you can see, Java provides significant control over many important aspects of the system.

Configuring Basic Security with Policy Files

To configure security in Java-based systems, you define a policy file and associate the file with a process at startup:

 java  Djava.security.policy=/usr/policy [rest of command]  

The -Djava.security.policy property designates the location and name of the policy file to associate with the command process.

Policy Files

Permissions are managed in a text file. The default policy file is named java.policy, and it's located in your user.home directory. You can determine your system locations, such as user.home, by compiling and running the following short program:

 class ListProperties  {   public static void main(String args[])   {     System.out.println("sun.boot.class.path = " + System.getProperty ("sun.boot.class. graphics/ccc.gifpath"));     System.out.println("java.class.path = " + System.getProperty ("java.class.path"));     System.out.println("user.home = " + System.getProperty("user.home"));     System.out.println("java.ext.dirs = " + System.getProperty ("java.ext.dirs"));   } } 

A policy file contains a list of entries or directives:

 grant [signedBy signers] [,codebase URL] {    permission permission_class [target][,action][,signedBy signers];   permission ... }; 

The parts of this code are as follows:

  • signers is replaced with the name of the entity or entities that have signed the code.

  • URL is replaced by the URL address of the location from where the code originated.

  • permission_class is the class type of the permission, such as java.net.SocketPermission or java.io.FilePermission.

  • target is the target identifier of the permission; for instance, the socket number or filename.

  • action is the permitted action of the permission, such as connect, listen, read, write, and so on.

If signers is omitted, then code coming from any signer will be granted the permission. Likewise, a missing URL will grant the permission to code coming from any location. The permission class AllPermission implies that all permissions are granted. As you can see, the policy file we've been using in the examples grants all permissions to any code from anywhere. This is clearly not desirable in a production environment.

Protection Domains

A protection domain is a set of permission entries associated with a code source. The code source includes the URL where the code originated, and optionally the certificates of the entities that have signed the code. Every Java class is uniquely associated with a code source. When an untrusted class is loaded, it is mapped to a protection domain based upon its code source and any signers it might have, as seen in Figure 2.2.

Figure 2.2. The protection domain of an untrusted class.

graphics/02fig02.gif

The grant entries in the policy file describe the permissions granted to the code source. Classes that have the same permissions but are from different code sources belong to different protection domains.

For example, to only grant permission to code signed from a specific user from a specific location, the following code would be used:

 grant signedBy "rflenner" codebase "http://www.jworkplace.org" {    permission permission_class [target][,action][,signedBy signers];   permission ... }; 

This could represent a very restrictive policy, because it specifies both signed user code and code origination for the permission given.

To grant only permission to code signed from a specific user from any location, you would use this:

 grant signedBy "rflenner" {    permission permission_class [target][,action][,signedBy signers];   permission ... }; 

To grant only permission from a specific location to any code, you would use this:

 grant codebase "file:${java.class.path}" {    permission permission_class [target][,action];   permission ... }; 

In this last example, you are specifying the local file system classpath as the location.



JavaT P2P Unleashed
JavaT P2P Unleashed
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 209

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net