About Me

My photo
"Enoughtheory.com" had its humble beginning in the year 2011 by ( Founder of Enoughtheory.com ) Mr Ravi Kant Soni , an Enterprise Java and Spring Framework Specialist, with a bachelor degree (B.E) in Information Science and Engineering from Reva Institute of Technology at Bangalore. He has been into the software development discipline for many years now. Ravi has worn many hats throughout his tenure, ranging from software development, designing multi-tenant applications, integration of new technology into an existing system, to his current love of writing a Spring Framework book. Currently, he is a lead engineer at HCL Technology. Ravi has focused on Web and Enterprise development using Spring Framework for most of his career and has been extensively involved in application design and implementation. He has developed applications for Core-Bank, HR and Payroll System, and e-Commerce systems using Spring Framework. Ravi Kant Soni is author of book "Learning Spring Application development" http://learningspringapplicationdevelopment.com

Friday, 20 July 2012

SaaS application development


SaaS application development

Software as a Service (SaaS) is a Software distribution model on which the data and associated files will be kept on the Main Server. Then it is possible to access the site using browser. By keeping the data and files on a centralized server, it only need to modify the files on a Single place and the changes will be reflected everywhere. Linked In, webex, Basecamp are examples of applications based on SaaS model. SaaS application is having the following architecture:
SaaS - Software as a service

Tenant

Tenant is the user or customer of the application. Usually SaaS uses Multi Tenant architecture that means, more than one user can use the same application. Single Tenant on which there is one user for the entire application.

SaaS Implementation

While developing an application using SaaS model, we have to consider few important things. This includes the database design changes, coding changes, authentication of Tenant, User, Loading User specific Theme & Content, SOA etc.

Authenticate Tenant

Once request received on Tenant Server, the very first thing which needs to do is to authenticate the Tenant. For this, we need to check the Host Name with our Tenant Database. If the Authentication is success, then the system receive the Tenant ID from database. We need to store this Tenant ID somewhere which can be accessible always either in SESSION or similar places.

User Authentication

  • Next steps is checking the User Authentication if needed. User may access the page with or without a role. For this, system uses Tenant ID.

Return Output

  • This contains a number of process.To get a result, it uses different independent components and achieve the result by using one or more of them together.
  • It may contact with database during this time.
  • In all cases, it carries the Tenant ID.

Database Structure

Keypoints which we need to consider while designing database are:
  • Design database tables to feel it like a separate website
  • Apply indexing
  • Always keep Tenant ID in all tables for easy database management

Service Oriented Architecture (SOA)

Important points to consider here are:
  • It need to split the functions as much as smaller independent components
  • The state of each function does not interact with other
  • Achieve each services with loosely coupled components

Theme Management

Multi Tenant Application needs option to change Theme by keeping the inner functionalities as same in some cases. The basic layout structure of a Multi Tenant SaaS application have three steps layouts.
  • Wrapper: This is common for the whole application.
  • Nested Layout: This contains the site specific details like logo, hard coded contents etc. We can use CMS (Content Management System) to change the Site Specific details.
  • View: The actual content requested will be displayed through this page.

Thursday, 19 July 2012

Multi-Tenant Data Architecture

Introduction

Trust, or the lack thereof, is the number one factor blocking the adoption of software as a service (SaaS). A case could be made that data is the most important asset of any business—data about products, customers, employees, suppliers, and more. And data, of course, is at the heart of SaaS. SaaS applications provide customers with centralized, network-based access to data with less overhead than is possible when using a locally-installed application. But in order to take advantage of the benefits of SaaS, an organization must surrender a level of control over its own data, trusting the SaaS vendor to keep it safe and away from prying eyes.
To earn this trust, one of the highest priorities for a prospective SaaS architect is creating a SaaS data architecture that is both robust and secure enough to satisfy tenants or clients who are concerned about surrendering control of vital business data to a third party, while also being efficient and cost-effective to administer and maintain.
This is the second article in our series about designing multi-tenant applications. The first article, Architecture Strategies for Catching the Long Tail, introduced the SaaS model at a high level and discussed its challenges and benefits. It is available on MSDN. Other articles in the series will focus on topics such as workflow and user interface design, overall security, and others.
In this article, we'll look at the continuum between isolated data and shared data, and identify three distinct approaches for creating data architectures that fall at different places along the continuum. Next, we'll explore some of the technical and business factors to consider when deciding which approach to use. Finally, we'll present design patterns for ensuring security, creating an extensible data model, and scaling the data infrastructure.

Three Approaches to Managing Multi-Tenant Data

The distinction between shared data and isolated data isn't binary. Instead, it's more of a continuum, with many variations that are possible between the two extremes.
Aa479086.mlttntda01(en-us,MSDN.10).gif
Data architecture is an area in which the optimal degree of isolation for a SaaS application can vary significantly depending on technical and business considerations. Experienced data architects are used to considering a broad spectrum of choices when designing an architecture to meet a specific set of challenges, and SaaS is certainly no exception. We shall examine three broad approaches, each of which lies at a different location in the continuum between isolation and sharing.
Aa479086.mlttntda02(en-us,MSDN.10).gif

Separate Databases

Storing tenant data in separate databases is the simplest approach to data isolation.
Aa479086.mlttntda03(en-us,MSDN.10).gif
Figure 1. This approach uses a different database for each tenant
Computing resources and application code are generally shared between all the tenants on a server, but each tenant has its own set of data that remains logically isolated from data that belongs to all other tenants. Metadata associates each database with the correct tenant, and database security prevents any tenant from accidentally or maliciously accessing other tenants' data.
Giving each tenant its own database makes it easy to extend the application's data model (discussed later) to meet tenants' individual needs, and restoring a tenant's data from backups in the event of a failure is a relatively simple procedure. Unfortunately, this approach tends to lead to higher costs for maintaining equipment and backing up tenant data. Hardware costs are also higher than they are under alternative approaches, as the number of tenants that can be housed on a given database server is limited by the number of databases that the server can support. (Using autoclose to unload databases from memory when there are no active connections can make an application more scalable by increasing the number of databases each server can support.)
Separating tenant data into individual databases is the "premium" approach, and the relatively high hardware and maintenance requirements and costs make it appropriate for customers that are willing to pay extra for added security and customizability. For example, customers in fields such as banking or medical records management often have very strong data isolation requirements, and may not even consider an application that does not supply each tenant with its own individual database.

Shared Database, Separate Schemas

Another approach involves housing multiple tenants in the same database, with each tenant having its own set of tables that are grouped into a schema created specifically for the tenant.
Aa479086.mlttntda04(en-us,MSDN.10).gif
Figure 2. In this approach each tenant has its own separate set of tables in a common database
When a customer first subscribes to the service, the provisioning subsystem creates a discrete set of tables for the tenant and associates it with the tenant's own schema. You can use the SQL CREATE command to create a schema and authorize a user account to access it. For example, in Microsoft SQL Server 2005:
CREATE SCHEMA ContosoSchema AUTHORIZATION Contoso

The application can then create and access tables within the tenant's schema using the SchemaName.TableName convention:
CREATE TABLE ContosoSchema.Resumes (EmployeeID int identity primary key, 
   Resume nvarchar(MAX))

ALTER USER Contoso WITH DEFAULT_SCHEMA = ContosoSchema

A tenant account can access tables within its default schema by specifying just the table name, instead of using the SchemaName.TableName convention. This way, a single set of SQL statements can be created for all tenants, which each tenant can use to access its own data:
SELECT * FROM Resumes

Like the isolated approach, the separate-schema approach is relatively easy to implement, and tenants can extend the data model as easily as with the separate-database approach. (Tables are created from a standard default set, but once they are created they no longer need to conform to the default set, and tenants may add or modify columns and even tables as desired.) This approach offers a moderate degree of logical data isolation for security-conscious tenants, though not as much as a completely isolated system would, and can support a larger number of tenants per database server.
A significant drawback of the separate-schema approach is that tenant data is harder to restore in the event of a failure. If each tenant has its own database, restoring a single tenant's data means simply restoring the database from the most recent backup. With a separate-schema application, restoring the entire database would mean overwriting the data of every tenant on the same database with backup data, regardless of whether each one has experienced any loss or not. Therefore, to restore a single customer's data, the database administrator may have to restore the database to a temporary server, and then import the customer's tables into the production server—a complicated and potentially time-consuming task.
The separate schema approach is appropriate for applications that use a relatively small number of database tables, on the order of about 100 tables per tenant or fewer. This approach can typically accommodate more tenants per server than the separate-database approach can, so you can offer the application at a lower cost, as long as your customers will accept having their data co-located with that of other tenants.

Shared Database, Shared Schema

A third approach involves using the same database and the same set of tables to host multiple tenants' data. A given table can include records from multiple tenants stored in any order; a Tenant ID column associates every record with the appropriate tenant.
Aa479086.mlttntda05(en-us,MSDN.10).gif
Figure 3. In this approach, all tenants share the same set of tables, and a Tenant ID associates each tenant with the rows that it owns
Of the three approaches explained here, the shared schema approach has the lowest hardware and backup costs, because it allows you to serve the largest number of tenants per database server. However, because multiple tenants share the same database tables, this approach may incur additional development effort in the area of security, to ensure that tenants can never access other tenants' data, even in the event of unexpected bugs or attacks.
The procedure for restoring data for a tenant is similar to that for the shared-schema approach, with the additional complication that individual rows in the production database must be deleted and then reinserted from the temporary database. If there are a very large number of rows in the affected tables, this can cause performance to suffer noticeably for all the tenants that the database serves.
The shared-schema approach is appropriate when it is important that the application be capable of serving a large number of tenants with a small number of servers, and prospective customers are willing to surrender data isolation in exchange for the lower costs that this approach makes possible.

Choosing an Approach

Each of the three approaches described above offers its own set of benefits and tradeoffs that make it an appropriate model to follow in some cases and not in others, as determined by a number of business and technical considerations. Some of these considerations are listed below.

Economic Considerations

Applications optimized for a shared approach tend to require a larger development effort than applications designed using a more isolated approach (because of the relative complexity of developing a shared architecture), resulting in higher initial costs. Because they can support more tenants per server, however, their ongoing operational costs tend to be lower.
Aa479086.mlttntda06(en-us,MSDN.10).gif
Figure 4. Cost over time for a hypothetical pair of SaaS applications; one uses a more isolated approach, while the other uses a more shared approach
Your development effort can be constrained by business and economic factors, which can influence your choice of approach. The shared schema approach can end up saving you money over the long run, but it does require a larger initial development effort before it can start producing revenue. If you are unable to fund a development effort of the size necessary to build a shared schema application, or if you need to bring your application to market more quickly than a large-scale development effort would allow, you may have to consider a more isolated approach.

Security Considerations

As your application will store sensitive tenant data, prospective customers will have high expectations about security, and your service level agreements (SLAs) will need to provide strong data safety guarantees. A common misconception holds that only physical isolation can provide an appropriate level of security. In fact, data stored using a shared approach can also provide strong data safety, but requires the use of more sophisticated design patterns.

Tenant Considerations

The number, nature, and needs of the tenants you expect to serve all affect your data architecture decision in different ways. Some of the following questions may bias you toward a more isolated approach, while others may bias you toward a more shared approach.
  • How many prospective tenants do you expect to target? You may be nowhere near being able to estimate prospective use with authority, but think in terms of orders of magnitude: are you building an application for hundreds of tenants? Thousands? Tens of thousands? More? The larger you expect your tenant base to be, the more likely you will want to consider a more shared approach.
  • How much storage space do you expect the average tenant's data to occupy? If you expect some or all tenants to store very large amounts of data, the separate-database approach is probably best. (Indeed, data storage requirements may force you to adopt a separate-database model anyway. If so, it will be much easier to design the application that way from the beginning than to move to a separate-database approach later on.)
  • How many concurrent end users do you expect the average tenant to support? The larger the number, the more appropriate a more isolated approach will be to meet end-user requirements.
  • Do you expect to offer any per-tenant value-added services, such as per-tenant backup and restore capability? Such services are easier to offer through a more isolated approach.
Aa479086.mlttntda07(en-us,MSDN.10).gif
Figure 5. Tenant-related factors and how they affect "isolated versus shared" data architecture decisions

Regulatory Considerations

Companies, organizations, and governments are often subject to regulatory law that can affect their security and record storage needs. Investigate the regulatory environments that your prospective customers occupy in the markets in which you expect to operate, and determine whether they present any considerations that will affect your decision.

Skill Set Considerations

Designing single-instance, multi-tenant architecture is still a very new skill, so subject matter expertise can be hard to come by. If your architects and support staff do not have a great deal of experience building SaaS applications, they will need to acquire the necessary knowledge, or you will have to hire people that already have it. In some cases, a more isolated approach may allow your staff to leverage more of its existing knowledge of traditional software development than a more shared approach would.

Realizing Multi-Tenant Data Architecture

The remainder of this article details a number of patterns that can help you plan and build your SaaS application. As we discussed in our introductory article, a well-designed SaaS application is distinguished by three qualities: scalability, configurability, and multi-tenant efficiency. The table below lists the patterns appropriate for each of the three approaches, divided into sections representing these three qualities.
Optimizing for multi-tenant efficiency in a shared environment must not compromise the level of security safeguarding data access. The security patterns listed below demonstrate how you can design an application with "virtual isolation" through mechanisms such as permissions, SQL views, and encryption.
Configurability allows SaaS tenants to alter the way the application appears and behaves without requiring a separate application instance for each individual tenant. The extensibility patterns describe possible ways you can implement a data model that tenants can extend and configure individually to meet their needs.
The approach you choose for your SaaS application's data architecture will affect the options available to you for scaling it to accommodate more tenants or heavier usage. The scalability patterns address the different challenges posed by scaling shared databases and dedicated databases.

Security Patterns

Trusted Database Connections

Aa479086.mlttntda08(en-us,MSDN.10).gif
Aa479086.mlttntda09(en-us,MSDN.10).gif
Aa479086.mlttntda10(en-us,MSDN.10).gif
This approach involves creating a database access account for each tenant, and using ACLs to grant each of these tenant accounts access to the database objects the tenant is allowed to use. When an end user performs an action that directly or indirectly requires a call to a database, the application uses credentials associated with the tenant account, rather than credentials associated with the end user. (One way for the application to obtain the proper credentials is through impersonation, in conjunction with a credentialing system like Kerberos. A second approach is to use a security token service that returns an actual set of encrypted login credentials established for the tenant, that the application process can then submit to the database.) The database server does not distinguish between requests originating from different end users associated with the same tenant, and grants all such requests access to the tenant's data. Within the application itself, security code prevents end users from receiving and modifying any data that they are not entitled to access.
For example, consider an end user of a customer relations management (CRM) application who performs an operation that queries the database for customer records matching a certain string. The application submits the query to the database using the security context of the tenant, so instead of returning all of the matching records in the database, the query only retrieves the matching rows from the tables the tenant is allowed to access. So far, so good—but suppose the end user's role only allows her to access records of customers located within a certain geographic region. (For more information about roles, see the section "Authorization" in Architecture Strategies for Catching the Long Tail, the first article in this series.) The application must intercept the query results and only present the user with the records that she is entitled to see.

Secure Database Tables

Tenant View Filter

Tenant Data Encryption

Cryptographic methods are categorized as either symmetric or asymmetric. In symmetric cryptography, a key is generated that is used to encrypt and decrypt data. Data encrypted with a symmetric key can be decrypted with the same key. In asymmetric cryptography (also called public-key cryptography), two keys are used, designated the public key and the private key. Data that is encrypted with a given public key can only be decrypted with the corresponding private key, and vice versa. Generally, public keys are distributed to any and all parties interested in communicating with the key holder, while private keys are held secure. For example, if Alice wishes to send an encrypted message to Bob, she obtains Bob's public key through some agreed-upon means, and uses it to encrypt the message. The resulting encrypted message, or cyphertext, can only be decrypted by someone in possession of Bob's private key (in practice, this should only be Bob). This way, Bob never has to share his private key with Alice. To send a message to Bob using symmetric encryption, Alice would have to send the symmetric key separately—which runs the risk that the key might be intercepted by a third party during transmission.

Extensibility Patterns

As designed, your application will naturally include a standard database setup, with default tables, fields, queries, and relationships that are appropriate to the nature of your solution. But different organizations have their own unique needs that a rigid, inextensible default data model won't be able to address. For example, one customer of a SaaS job-tracking system might have to store an externally generated classification code string with each record to fully integrate the system with their other processes. A different customer may have no need for a classification string field, but might require support for tracking a category ID number, an integer. Therefore, in many cases you will have to develop and implement a method by which customers can extend your default data model to meet their needs, without affecting the data model that other customers use.

Preallocated Fields

Aa479086.mlttntda11(en-us,MSDN.10).gif
Aa479086.mlttntda12(en-us,MSDN.10).gif
Aa479086.mlttntda13(en-us,MSDN.10).gif

Name-Value Pairs

Aa479086.mlttntda14(en-us,MSDN.10).gif

Custom Columns

Aa479086.mlttntda15(en-us,MSDN.10).gif
This pattern is appropriate for separate-database or separate-schema applications, because each tenant has its own set of tables that can be modified independently of those belonging to any other clients. From a data model standpoint, this is the simplest of the three extensibility patterns, because it does not require you to track data extensions separately. On the application architecture side, though, this pattern can sometimes be more difficult to implement, because it allows tenants to vary the number of columns in a table. Even if the Custom Columns pattern is available to you, you may consider using a variation on the Preallocated Fields or Name-Value Pairs pattern to reduce development effort, allowing you to write application code that can assume a known and unchanging number of fields in each table.

Using Data Model Extensions

Scalability Patterns

Large-scale enterprise software is intended to be used by thousands of people simultaneously. If you have experience building enterprise applications of this sort, you know first-hand the challenges of creating a scalable architecture. For a SaaS application, scalability is even more important, because you'll have to support data belonging to all your customers. For independent software vendors (ISVs) accustomed to building on-premise enterprise software, supporting this kind of user base is like moving from the minor leagues to the majors: the rules may be familiar, but the game is played on an entirely different level. Instead of a widely deployed, business-critical enterprise application, you're really building an Internet-scale system that needs to actively support a user base potentially numbering in the millions.

Scaling Techniques

Tenant-Based Horizontal Partitioning

Single Tenant Scaleout

Conclusion

The design approaches and patterns we've discussed in this article should help you create the foundation layer of trust that's vital to the success of your SaaS application. Designing a SaaS data architecture that reconciles the competing benefits and demands of sharing and isolation isn't a trivial task, but these approaches and patterns should help you identify and resolve many of the critical questions you will face. The ideas and recommendations presented here differ in the details, but they all help you leverage the principles of configurability, scalability, and multi-tenant efficiency to design a secure and extensible data architecture for a SaaS application.

Tuesday, 17 July 2012

Java virtual machine performs thread synchronization

How the Java virtual machine performs thread synchronization

Understanding threads, locks, monitors and more in Java bytecode

Threads and shared data

One of the strengths of the Java programming language is its support for multithreading at the language level. Much of this support centers on coordinating access to data shared among multiple threads.
The JVM organizes the data of a running Java application into several runtime data areas: one or more Java stacks, a heap, and a method area.

Inside the Java virtual machine, each thread is awarded a Java stack, which contains data no other thread can access, including the local variables, parameters, and return values of each method the thread has invoked. The data on the stack is limited to primitive types and object references. In the JVM, it is not possible to place the image of an actual object on the stack. All objects reside on the heap.
There is only one heap inside the JVM, and all threads share it. The heap contains nothing but objects. There is no way to place a solitary primitive type or object reference on the heap -- these things must be part of an object. Arrays reside on the heap, including arrays of primitive types, but in Java, arrays are objects too.
Besides the Java stack and the heap, the other place data may reside in the JVM is the method area, which contains all the class (or static) variables used by the program. The method area is similar to the stack in that it contains only primitive types and object references. Unlike the stack, however, the class variables in the method area are shared by all threads.

Object and class locks

As described above, two memory areas in the Java virtual machine contain data shared by all threads. These are:
  • The heap, which contains all objects
  • The method area, which contains all class variables
If multiple threads need to use the same objects or class variables concurrently, their access to the data must be properly managed. Otherwise, the program will have unpredictable behavior.
To coordinate shared data access among multiple threads, the Java virtual machine associates a lock with each object and class. A lock is like a privilege that only one thread can "possess" at any one time. If a thread wants to lock a particular object or class, it asks the JVM. At some point after the thread asks the JVM for a lock -- maybe very soon, maybe later, possibly never -- the JVM gives the lock to the thread. When the thread no longer needs the lock, it returns it to the JVM. If another thread has requested the same lock, the JVM passes the lock to that thread.
Class locks are actually implemented as object locks. When the JVM loads a class file, it creates an instance of class java.lang.Class. When you lock a class, you are actually locking that class's Class object.
Threads need not obtain a lock to access instance or class variables. If a thread does obtain a lock, however, no other thread can access the locked data until the thread that owns the lock releases it.

Monitors

The JVM uses locks in conjunction with monitors. A monitor is basically a guardian in that it watches over a sequence of code, making sure only one thread at a time executes the code.
Each monitor is associated with an object reference. When a thread arrives at the first instruction in a block of code that is under the watchful eye of a monitor, the thread must obtain a lock on the referenced object. The thread is not allowed to execute the code until it obtains the lock. Once it has obtained the lock, the thread enters the block of protected code.
When the thread leaves the block, no matter how it leaves the block, it releases the lock on the associated object.

Multiple locks

A single thread is allowed to lock the same object multiple times. For each object, the JVM maintains a count of the number of times the object has been locked. An unlocked object has a count of zero. When a thread acquires the lock for the first time, the count is incremented to one. Each time the thread acquires a lock on the same object, a count is incremented. Each time the thread releases the lock, the count is decremented. When the count reaches zero, the lock is released and made available to other threads.

Synchronized blocks

In Java language terminology, the coordination of multiple threads that must access shared data is called synchronization. The language provides two built-in ways to synchronize access to data: with synchronized statements or synchronized methods.

Synchronized statements

To create a synchronized statement, you use the synchronized keyword with an expression that evaluates to an object reference, as in the reverseOrder() method below:

class KitchenSync {
    private int[] intArray = new int[10];
    void reverseOrder() {
        synchronized (this) {
            int halfWay = intArray.length / 2;
            for (int i = 0; i < halfWay; ++i) {
                int upperIndex = intArray.length - 1 - i;
                int save = intArray[upperIndex];
                intArray[upperIndex] = intArray[i];
                intArray[i] = save;
            }
        }
    }
}
In the case above, the statements contained within the synchronized block will not be executed until a lock is acquired on the current object (this). If instead of a this reference, the expression yielded a reference to another object, the lock associated with that object would be acquired before the thread continued.
Two opcodes, monitorenter and monitorexit, are used for synchronization blocks within methods, as shown in the table below.

Table 1. Monitors


Opcode

Operand(s)

Description

monitorenter

none
pop objectref, acquire the lock associated with objectref

monitorexit

none
pop objectref, release the lock associated with objectref
When monitorenter is encountered by the Java virtual machine, it acquires the lock for the object referred to by objectref on the stack. If the thread already owns the lock for that object, a count is incremented. Each time monitorexit is executed for the thread on the object, the count is decremented. When the count reaches zero, the monitor is released.
Take a look at the bytecode sequence generated by the reverseOrder() method of the KitchenSync class.
Note that a catch clause ensures the locked object will be unlocked even if an exception is thrown from within the synchronized block. No matter how the synchronized block is exited, the object lock acquired when the thread entered the block definitely will be released.

Synchronized methods

To synchronize an entire method, you just include the synchronized keyword as one of the method qualifiers, as in:
class HeatSync {
    private int[] intArray = new int[10];
    synchronized void reverseOrder() {
        int halfWay = intArray.length / 2;
        for (int i = 0; i < halfWay; ++i) {
            int upperIndex = intArray.length - 1 - i;
            int save = intArray[upperIndex];
            intArray[upperIndex] = intArray[i];
            intArray[i] = save;
        }
    }
}
The JVM does not use any special opcodes to invoke or return from synchronized methods. When the JVM resolves the symbolic reference to a method, it determines whether the method is synchronized. If it is, the JVM acquires a lock before invoking the method. For an instance method, the JVM acquires the lock associated with the object upon which the method is being invoked. For a class method, it acquires the lock associated with the class to which the method belongs. After a synchronized method completes, whether it completes by returning or by throwing an exception, the lock is released.


Object Finalization and Cleanup

Object finalization and cleanup

How to design classes for proper object cleanup

  In this Design Techniques article, I'll be focusing on the design principles that help you ensure proper cleanup at the end of an object's life.

Why clean up?

Every object in a Java program uses computing resources that are finite. Most obviously, all objects use some memory to store their images on the heap. (This is true even for objects that declare no instance variables. Each object image must include some kind of pointer to class data, and can include other implementation-dependent information as well.) But objects may also use other finite resources besides memory. For example, some objects may use resources such as file handles, graphics contexts, sockets, and so on. When you design an object, you must make sure it eventually releases any finite resources it uses so the system won't run out of those resources.
Because Java is a garbage-collected language, releasing the memory associated with an object is easy. All you need to do is let go of all references to the object. Because you don't have to worry about explicitly freeing an object, as you must in languages such as C or C++, you needn't worry about corrupting memory by accidentally freeing the same object twice. You do, however, need to make sure you actually release all references to the object. If you don't, you can end up with a memory leak, just like the memory leaks you get in a C++ program when you forget to explicitly free objects. Nevertheless, so long as you release all references to an object, you needn't worry about explicitly "freeing" that memory.
Similarly, you needn't worry about explicitly freeing any constituent objects referenced by the instance variables of an object you no longer need. Releasing all references to the unneeded object will in effect invalidate any constituent object references contained in that object's instance variables. If the now-invalidated references were the only remaining references to those constituent objects, the constituent objects will also be available for garbage collection. Piece of cake, right?

The rules of garbage collection

Although garbage collection does indeed make memory management in Java a lot easier than it is in C or C++, you aren't able to completely forget about memory when you program in Java. To know when you may need to think about memory management in Java, you need to know a bit about the way garbage collection is treated in the Java specifications.

Garbage collection is not mandated
The first thing to know is that no matter how diligently you search through the Java Virtual Machine Specification (JVM Spec), you won't be able to find any sentence that commands, Every JVM must have a garbage collector. The Java Virtual Machine Specification gives VM designers a great deal of leeway in deciding how their implementations will manage memory, including deciding whether or not to even use garbage collection at all. Thus, it is possible that some JVMs (such as a bare-bones smart card JVM) may require that programs executed in each session "fit" in the available memory.
Of course, you can always run out of memory, even on a virtual memory system. The JVM Spec does not state how much memory will be available to a JVM. It just states that whenever a JVM does run out of memory, it should throw an OutOfMemoryError.
Nevertheless, to give Java applications the best chance of executing without running out of memory, most JVMs will use a garbage collector. The garbage collector reclaims the memory occupied by unreferenced objects on the heap, so that memory can be used again by new objects, and usually de-fragments the heap as the program runs.

Garbage collection algorithm is not defined
Another command you won't find in the JVM specification is All JVMs that use garbage collection must use the XXX algorithm. The designers of each JVM get to decide how garbage collection will work in their implementations. Garbage collection algorithm is one area in which JVM vendors can strive to make their implementation better than the competition's. This is significant for you as a Java programmer for the following reason:
Because you don't generally know how garbage collection will be performed inside a JVM, you don't know when any particular object will be garbage collected.
So what? you might ask. The reason you might care when an object is garbage collected has to do with finalizers. (A finalizer is defined as a regular Java instance method named finalize() that returns void and takes no arguments.) The Java specifications make the following promise about finalizers:
Before reclaiming the memory occupied by an object that has a finalizer, the garbage collector will invoke that object's finalizer.
Given that you don't know when objects will be garbage collected, but you do know that finalizable objects will be finalized as they are garbage collected, you can make the following grand deduction:
You don't know when objects will be finalized.
You should imprint this important fact on your brain and forever allow it to inform your Java object designs.

Finalizers to avoid

The central rule of thumb concerning finalizers is this:
Don't design your Java programs such that correctness depends upon "timely" finalization.
In other words, don't write programs that will break if certain objects aren't finalized by certain points in the life of the program's execution. If you write such a program, it may work on some implementations of the JVM but fail on others.

Don't rely on finalizers to release non-memory resources
An example of an object that breaks this rule is one that opens a file in its constructor and closes the file in its finalize() method. Although this design seems neat, tidy, and symmetrical, it potentially creates an insidious bug. A Java program generally will have only a finite number of file handles at its disposal. When all those handles are in use, the program won't be able to open any more files.
A Java program that makes use of such an object (one that opens a file in its constructor and closes it in its finalizer) may work fine on some JVM implementations. On such implementations, finalization would occur often enough to keep a sufficient number of file handles available at all times. But the same program may fail on a different JVM whose garbage collector doesn't finalize often enough to keep the program from running out of file handles. Or, what's even more insidious, the program may work on all JVM implementations now but fail in a mission-critical situation a few years (and release cycles) down the road.

Other finalizer rules of thumb
Two other decisions left to JVM designers are selecting the thread (or threads) that will execute the finalizers and the order in which finalizers will be run. Finalizers may be run in any order -- sequentially by a single thread or concurrently by multiple threads. If your program somehow depends for correctness on finalizers being run in a particular order, or by a particular thread, it may work on some JVM implementations but fail on others.
You should also keep in mind that Java considers an object to be finalized whether the finalize() method returns normally or completes abruptly by throwing an exception. Garbage collectors ignore any exceptions thrown by finalizers and in no way notify the rest of the application that an exception was thrown. If you need to ensure that a particular finalizer fully accomplishes a certain mission, you must write that finalizer so that it handles any exceptions that may arise before the finalizer completes its mission.
One more rule of thumb about finalizers concerns objects left on the heap at the end of the application's lifetime. By default, the garbage collector will not execute the finalizers of any objects left on the heap when the application exits. To change this default, you must invoke the runFinalizersOnExit() method of class Runtime or System, passing true as the single parameter. If your program contains objects whose finalizers must absolutely be invoked before the program exits, be sure to invoke runFinalizersOnExit() somewhere in your program.

So what are finalizers good for?

By now you may be getting the feeling that you don't have much use for finalizers. While it is likely that most of the classes you design won't include a finalizer, there are some reasons to use finalizers.
One reasonable, though rare, application for a finalizer is to free memory allocated by native methods. If an object invokes a native method that allocates memory (perhaps a C function that calls malloc()), that object's finalizer could invoke a native method that frees that memory (calls free()). In this situation, you would be using the finalizer to free up memory allocated on behalf of an object -- memory that will not be automatically reclaimed by the garbage collector.
Another, more common, use of finalizers is to provide a fallback mechanism for releasing non-memory finite resources such as file handles or sockets. As mentioned previously, you shouldn't rely on finalizers for releasing finite non-memory resources. Instead, you should provide a method that will release the resource. But you may also wish to include a finalizer that checks to make sure the resource has already been released, and if it hasn't, that goes ahead and releases it. Such a finalizer guards against (and hopefully will not encourage) sloppy use of your class. If a client programmer forgets to invoke the method you provided to release the resource, the finalizer will release the resource if the object is ever garbage collected. The finalize() method of the LogFileManager class, shown later in this article, is an example of this kind of finalizer.

Avoid finalizer abuse

The existence of finalization produces some interesting complications for JVMs and some interesting possibilities for Java programmers. What finalization grants to programmers is power over the life and death of objects. In short, it is possible and completely legal in Java to resurrect objects in finalizers -- to bring them back to life by making them referenced again. (One way a finalizer could accomplish this is by adding a reference to the object being finalized to a static linked list that is still "live.") Although such power may be tempting to exercise because it makes you feel important, the rule of thumb is to resist the temptation to use this power. In general, resurrecting objects in finalizers constitutes finalizer abuse.
The main justification for this rule is that any program that uses resurrection can be redesigned into an easier-to-understand program that doesn't use resurrection. A formal proof of this theorem is left as an exercise to the reader (I've always wanted to say that), but in an informal spirit, consider that object resurrection will be as random and unpredictable as object finalization. As such, a design that uses resurrection will be difficult to figure out by the next maintenance programmer who happens along -- who may not fully understand the idiosyncrasies of garbage collection in Java.
If you feel you simply must bring an object back to life, consider cloning a new copy of the object instead of resurrecting the same old object. The reasoning behind this piece of advice is that garbage collectors in the JVM invoke the finalize() method of an object only once. If that object is resurrected and becomes available for garbage collection a second time, the object's finalize() method will not be invoked again.

Managing non-memory resources

Because heap memory is automatically reclaimed by the garbage collector, the main thing you need to worry about when you design an object's end-of-lifetime behavior is to ensure that finite non-memory resources, such as file handles or sockets, are released. You can take any of three basic approaches when you design an object that needs to use a finite non-memory resource:
  1. Obtain and release the resource within each method that needs the resource
  2. Provide a method that obtains the resource and another that releases it
  3. Obtain the resource at creation time and provide a method that releases it

Approach 1: Obtain and release within each relevant method

As a general rule, the releasing of non-memory finite resources should be done as soon as possible after their use because the resources are, by definition, finite. If possible, you should try to obtain a resource, use it, then release it all within the method that needs the resource.
A log file class: An example of Approach 1
An example of a class where Approach 1 might make sense is a log file class. Such a class takes care of formatting and writing log messages to a file. The name of the log file is passed to the object as it is instantiated. To write a message to the log file, a client invokes a method in the log file class, passing the message as a String. Here's an example:
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;
class LogFile {
    private String fileName;
    LogFile(String fileName) {
        this.fileName = fileName;
    }
    // The writeToFile() method will catch any IOException
    // so that clients aren't forced to catch IOException
    // everywhere they write to the log file.  For now,
    // just fail silently. In the future, could put
    // up an informative non-modal dialog box that indicates
    // a logging error occurred. - bv 4/15/98
    void writeToFile(String message) {
        FileOutputStream fos = null;
        PrintWriter pw = null;
        try {
            fos = new FileOutputStream(fileName, true);
            try {
                pw = new PrintWriter(fos, false);
                pw.println("------------------");
                pw.println(message);
                pw.println();
            }
            finally {
                if (pw != null) {
                    pw.close();
                }
            }
        }
        catch (IOException e) {
        }
        finally {
            if (fos != null) {
                try {
                    fos.close();
                }
                catch (IOException e) {
                }
            }
        }
    }
}


Class LogFile is a simple example of Approach 1. A more production-ready LogFile class might do things such as:
  • Insert the date and time each log message was written
  • Allow messages to be assigned a level of importance (such as ERROR, INFO, or DEBUG) and enable a level to be set that will prevent unwanted detail (such as DEBUG messages) from making it into the log file
  • Manage in some way the size of the log file, i.e., by copying it to a different filename and starting fresh each time the log file achieves a certain size


The main feature of this simple version of class LogFile is that it surrounds each log message with a series of dashes and a blank line.

Using finally to ensure resource release
Note that in the writeToFile() method, the releasing of the resource is done in finally clauses. This is to make sure the finite resource (file handle) is actually released no matter how the code is exited. If an IOException is thrown, the file will be closed.

Pros and cons of Approach 1
The approach to resource management taken by class LogFile (Approach 1 from the above list) helps make your class easy to use, because client programmers don't have to worry about explicitly obtaining or releasing the resource. In both Approach 2 and 3 from the list above client programmers must remember to explicitly invoke a method to release the resource. In addition -- and what can be far more difficult -- client programmers must figure out when their programs no longer need a resource.
A problem with Approach 1 is that obtaining and releasing the resource each time you need it may be too inefficient. Another problem is that, in some situations, you may need to hold onto the resource between invocations of methods that use the resource (such as writeToFile()), so no other object can have access to it. In such cases, one of the other two approaches is preferable.

Approach 2: Offer methods for obtaining and releasing resources

In Approach 2 from the list above, you provide one method for obtaining the resource and another method for releasing it. This approach enables the same class instance to obtain and release a resource multiple times. Here's an example:

import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;
class LogFileManager {
    private FileOutputStream fos;
    private PrintWriter pw;
    private boolean logFileOpen = false;
    LogFileManager() {
    }
    LogFileManager(String fileName) throws IOException {
        openLogFile(fileName);
    }
    void openLogFile(String fileName) throws IOException {
        if (!logFileOpen) {
            try {
                fos = new FileOutputStream(fileName, true);
                pw = new PrintWriter(fos, false);
                logFileOpen = true;
            }
            catch (IOException e) {
                if (pw != null) {
                    pw.close();
                    pw = null;
                }
                if (fos != null) {
                    fos.close();
                    fos = null;
                }
                throw e;
            }
        }
    }
    void closeLogFile() throws IOException {
        if (logFileOpen) {
            pw.close();
            pw = null;
            fos.close();
            fos = null;
            logFileOpen = false;
        }
    }
    boolean isOpen() {
        return logFileOpen;
    }
    void writeToFile(String message) throws IOException {
        pw.println("------------------");
        pw.println(message);
        pw.println();
    }
    protected void finalize() throws Throwable {
        if (logFileOpen) {
            try {
                closeLogFile();
            }
            finally {
                super.finalize();
            }
        }
    }
}


In this example, class LogFileManager declares methods openLogFile() and closeLogFile(). Given this design, you could write to multiple log files with one instance of this class. This design also allows a client to monopolize the resource for as long as it wants. A client can write several consecutive messages to the log file without fear that another thread or process will slip in any intervening messages. Once a client successfully opens a log file with openLogFile(), that log file belongs exclusively to that client until the client invokes closeLogFile().
Note that LogFileManager uses a finalizer as a fallback in case a client forgets to invoke closeLogFile(). As mentioned earlier in this article, this is one of the more common uses of finalizers.
Note also that after invoking closeLogFile(), LogFileManager's finalizer invokes super.finalize(). Invoking superclass finalizers is good practice in any finalizer, even in cases (such as this) where no superclass exists other than Object. The JVM does not automatically invoke superclass finalizers, so you must do so explicitly. If someone ever inserts a class that declares a finalizer between LogFileManager and Object in the inheritance hierarchy, the new object's finalizer will already be invoked by LogFileManager's existing finalizer.
Making super.finalize() the last action of a finalizer ensures that subclasses will be finalized before superclasses. Although in most cases the placement of super.finalize() won't matter, in some rare cases, a subclass finalizer may require that its superclass be as yet unfinalized. So, as a general rule of thumb, place super.finalize() last.

Approach 3: Claim resource on creation, offer method for release

In the last approach, Approach 3 from the above list, the object obtains the resource upon creation and declares a method that releases the resource. Here's an example:
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.IOException;
class LogFileTransaction {
    private FileOutputStream fos;
    private PrintWriter pw;
    private boolean logFileOpen = false;
    LogFileTransaction(String fileName) throws IOException {
        try {
            fos = new FileOutputStream(fileName, true);
            pw = new PrintWriter(fos, false);
            logFileOpen = true;
        }
        catch (IOException e) {
            if (pw != null) {
                pw.close();
                pw = null;
            }
            if (fos != null) {
                fos.close();
                fos = null;
            }
            throw e;
        }
    }
    void closeLogFile() throws IOException {
        if (logFileOpen) {
            pw.close();
            pw = null;
            fos.close();
            fos = null;
            logFileOpen = false;
        }
    }
    boolean isOpen() {
        return logFileOpen;
    }
    void writeToFile(String message) throws IOException {
        pw.println("------------------");
        pw.println(message);
        pw.println();
    }
    protected void finalize() throws Throwable {
        if (logFileOpen) {
            try {
                closeLogFile();
            }
            finally {
                super.finalize();
            }
        }
    }
}


This class is called LogFileTransaction because every time a client wants to write a chunk of messages to the log file (and then let others use that log file), it must create a new LogFileTransaction. Thus, this class models one transaction between the client and the log file.
One interesting thing to note about Approach 3 is that this is the approach used by the FileOutputStream and PrintWriter classes used by all three example log file classes. In fact, if you look through the java.io package, you'll find that almost all of the java.io classes that deal with file handles use Approach 3. (The two exceptions are PipedReader and PipedWriter, which use Approach 2.)

Conclusion

The most important point to take away from this article is that if a Java object needs to take some action at the end of its life, no automatic way exists in Java that will guarantee that action is taken in a timely manner. You can't rely on finalizers to take the action, at least not in a timely way. You will need to provide a method that performs the action and encourage client programmers to invoke the method when the object is no longer needed.
This article contained several guidelines that pertain to finalizers:
  • Don't design your Java programs such that correctness depends on "timely" finalization
  • Don't assume that a finalizer will be run by any particular thread
  • Don't assume that finalizers will be run in any particular order
  • Avoid designs that require finalizers to resurrect objects; if you must use resurrection, prefer cloning over straight resurrection
  • Remember that exceptions thrown by finalizers are ignored
  • If your program includes objects with finalizers that absolutely must be run before the program exits, invoke runFinalizersOnExit(true) in class Runtime or System
  • Unless you are writing the finalizer for class Object, always invoke super.finalize() at the end of your finalizers
  •  
A request for reader participation

Software design is subjective. Your idea of a well-designed program may be your colleague's maintenance nightmare. In light of this fact, I am trying to make this column as interactive as possible.
I encourage your comments, criticisms, suggestions, flames -- all kinds of feedback -- about the material presented in this column. If you disagree with something, or have something to add, please let me know.