Take a few minutes to peruse and get familiar with the Project Zero Web site. You can join the Project Zero community, contribute to the project, or join the discussion forum, where you can comment on the project through each stage of its development. This article assumes that you have a suitable Java Development Kit (JDK) installed on your machine. You also need to be familiar with the concepts of PHP.
Get started with Project Zero, WebSphere sMash, and PHP is recommended reading. It shows how to download WebSphere sMash and create a PHP application. This article assumes you have a working version of WebSphere sMash with PHP. Note it is only necessary to work through the steps in that article up to the "Running the application" section.
Editor's note: IBM® WebSphere sMash and IBM WebSphere sMash Developer Edition are based on the highly acclaimed Project Zero incubator project. Project Zero is the development community for WebSphere sMash and will continue to offer developers a cost-free platform for developing applications with the latest builds, the latest features, and the support of the community.
The article shows how to access Java classes from PHP using the Java Bridge. It discusses calling Java methods and accessing fields, both instance and static. It also covers exception handling and type conversion between the PHP and Java worlds.
ZSL, WebSphere sMash, and Apache Lucene
For a real world example, this article steps through the creation of a simple search engine written in PHP that can index and search files using Apache Lucene. Apache Lucene is a high-performance, full-featured text search engine library written entirely in Java. It is a technology suitable for many applications that require full-text search.
ZSL used Apache Lucene in a WebSphere sMash application they wrote. ZSL® wanted to improve information sharing between their developers. To solve this problem, they put together a mashup to index their source code and documentation library (PDF, PowerPoint, Word, Excel, and many others). The application provides quick and easy access to code snippets from across the company.
Creating an application in WebSphere sMash
The first step to get started is to create a new project in Eclipse:
- Select File -> New -> Project... and expand the Zero category in the dialog.
- Select WebSphere sMash PHP Application and click Next as shown in Figure 1.
- Give your project a name (for example,
MyJavaProject) and click Finish. Your project is now created.
Figure 1. Create a new WebSphere sMash project dialog
Creating and calling Java objects
Next, write a PHP script that creates and calls a Java object:
- Right-click on the public folder and select New -> File.
- Give your file a name (for example,
Java.php) and click Finish. - Add the following code into the file:
<?php $file = new Java("java.io.File", __FILE__, FALSE); var_dump($file); var_dump($file->isDirectory()); ?> - Run the sample code by right-clicking on the project name in Eclipse and select Run As -> WebSphere sMash Application.
- A Web server is started on port 8080 of your localhost.
- You can now go to a browser and direct it to
http://localhost:8080/Java.php, and you see the following output as shown in Figure 2.
Figure 2. Web browser output from calling Java objects
This sample code shows a PHP script using the built in Java class. The Java class
creates an instance of a Java class and calls the best matching constructor,
passing any arguments from the script. In this example, the root directory is
"/" and FALSE. The script
stores it in a PHP variable called $file. The script
then calls methods on the object just as if it was a normal PHP object, so in this
example we call it the isDirectory method.
This capability is powerful and gives PHP scripts access to any Java class. Note
that the Java class must be on the application class path
java.io.File is part of the core Java class library
and so it is always available.
Using the Java collection classes
Java has a rich set of collection classes, including maps, sets, lists, and
queues. This sample code shows how a PHP script can leverage those classes. As
before, create a new PHP script (for example,
MoreJava.php) and add the following code:
<?php
$map = new Java("java.util.HashMap");
$map->put("title", "Java Bridge!");
$array = array(1, 2, 3, 4, 5);
$map->put("stuff", $array);
var_dump($map->get("stuff"));
echo $map->get("title");
?>
|
You can now go to a browser and direct it to
http://localhost:8080/MoreJava.php, and you see the
following output as shown in Figure 3.
Figure 3. Web browser output from using the Java collection classes
The PHP script:
- Creates an instance of a Java
HashMapclass. - Stores a string containing
Java Bridge!in the map. - Highlights interoperability between Java and PHP types.
- Creates a PHP array and stores it in the Java map as shown in the code
below.
$array = array(1, 2, 3, 4, 5); $map->put("stuff", $array);
When the put call is invoked on the map, the PHP array
is converted to its closest equivalent Java type, which is a Java
Map. Likewise, when the get
call reads the value back from $map, it is converted
back to a regular PHP array. This is possible without any copying because PHP
arrays have two personalities, PHP arrays and Java maps.
Iterating over Java collections
Try replacing the MoreJava.php script with the
following code:
<?php
$list = new Java("java.util.ArrayList");
var_dump($list);
$date = new Java("java.util.Date", 70, 9, 4);
echo "<br/>";
$list->add("Java Bridge!");
$list->add($date);
$list->add(array(1, 2, 3, 4, 5));
$iterator = $list->iterator();
while ($iterator->hasNext() == TRUE) {
var_dump($iterator->next()); echo "<br/>";
}
?>
|
You can now go to a browser and direct it to
http://localhost:8080/MoreJava.php, and you see the
following output as shown in Figure 4.
Figure 4. Web browser output from iterating over Java collections
This example shows PHP using a Java ArrayList class.
Furthermore, it also gets an iterator from the
ArrayList and scans through the collection from start
to finish. The contents of the iterator are written in order, starting with
the string Java Bridge!, then the Java
Date object, and finishing with the PHP array
containing five numbers.
Accessing static methods and fields
Static methods and fields are accessed using
JavaClass. This is a little different to Java, where
static methods and fields are accessed directly using the class name. The
following code shows how to call currentTimeMillis on
java.lang.System:
<?php
$system = new JavaClass("java.lang.System");
var_dump($system);
echo("</br>Current time: ".
$system->currentTimeMillis()."</br>");
?>
|
Figure 5 shows the ouput from running this script in a browser.
Figure 5. Web browser output from accessing static methods
Accessing static fields is similar. The following code displays the
MIN_VALUE static field in the
java.lang.Integer class:
<?php
$integerClass = new JavaClass("java.lang.Integer");
var_dump($integerClass->MIN_VALUE);
?>
|
Figure 6 shows the ouput from running this script in a browser.
Figure 6. Web browser output from accessing static fields
Catching Java exceptions in PHP
The Java Bridge converts Java exceptions into instances of
JavaException. This is a generic PHP exception class
that is caught in PHP scripts. The following code snippet shows an invalid call to
getProperty on
java.lang.System:
<?php
try {
$system = new JavaClass("java.lang.System");
$system->getProperty(FALSE);
} catch (JavaException $exception) {
echo "Cause: ".$exception->getCause();
}
?>
|
Figure 7 shows the ouput from running this script in a browser.
Figure 7. Web browser output from catching Java exceptions
Note that in WebSphere sMash 1.0, the getCause method
returns the class name of the underlying Java exception, not the causing
exception itself. In the latest Project Zero builds, this oddity has been fixed to
return the actual Java exception.
Type conversion from Java to PHP
Table 1 shows how Java types are converted to PHP types. The general approach is
to convert to a type that minimizes any potential loss (for example, when
converting an int to a
byte). Note also that the conversions apply equally for
boxed and unboxed Java types, such as Integer and
int.
Table 1. Type conversion from Java to PHP
| Java type | PHP type | Comments |
null
|
null
| |
Integer/int |
int
| |
Double/double |
double
| |
Boolean/boolean |
bool
| |
Byte/byte |
int
| |
Character/char |
int
| |
Short/short |
int
| |
Long/long |
int
| |
Float/float |
double
| |
byte[]
|
string
| |
String
|
string
| The PHP string is encoded using the runtime encoding. |
Map
|
array
| The types of individual elements are converted as per this table, including nested maps. |
Object[]
|
array
| See the array conversion. |
| Anything Else! | n/a | This is the wrapped by a Java Bridge and becomes a generic PHP object. |
More information about type conversion is available at the Project Zero Web site.
The Java Bridge is intended to be a simple way for PHP scripts to use Java classes. With that in mind, there are several more advanced features that it does not contain. The most significant of these is calling overloaded methods reliably.
The Java Bridge selects a method or constructor based solely on the number of arguments supplied. If more than one possibility exists, then the Java Bridge selects the first one and tries that. This is extremely simplistic and leads to an exception being thrown when a constructor or method is called with the wrong argument types.
Selecting overloads with signatures
The problem with selecting a suitable overload has been solved in the latest
Project Zero builds (this is not available in WebSphere sMash 1.0) with the
addition of a new JavaSignature class. The JavaSignature allows a script to specify exactly which
constructor or method is invoked by defining the argument types to look for the
following:
<?php
$signature = new JavaSignature(JAVA_STRING);
$string = new Java("java.lang.String",
$signature, "Hello World!");
var_dump($string->toLowerCase());
var_dump($string->split(" "));
var_dump($string->toUpperCase());
?>
|
The arguments for JavaSignature are drawn from the
following PHP constants:
- JAVA_BOOLEAN
- JAVA_BYTE
- JAVA_CHAR
- JAVA_SHORT
- JAVA_INT
- JAVA_LONG
- JAVA_FLOAT
- JAVA_DOUBLE
- JAVA_STRING
- JAVA_OBJECT
In the previous example, the example selects a constructor on
java.lang.String that takes a single Java
String as its argument
(JAVA_STRING). Multiple arguments are comma separated,
for example, newJavaSignature(JAVA_STRING, JAVA_INT). You can specify
arrays of Java types using the JAVA_ARRAY modifier. For
example, the following selects an array of strings: newJavaSignature(JAVA_STRING | JAVA_ARRAY).
The following snippet shows a JavaSignature selecting
an overload of the valueOf method on
java.lang.String. Note how the signature is passed as
the first argument to the method call. The Java Bridge knows to check there for
signatures.
<?php
$class = new JavaClass("java.lang.String");
$signature = new JavaSignature(JAVA_INT);
var_dump($class->valueOf($signature, 1234567890));
?>
|
Methods in PHP are not case-sensitive, while Java is case-sensitive. The Java Bridge is case-sensitive and so the PHP method name must match the Java method name exactly.
Java developers are used to invoking static methods and fields using the class
name (for example, Integer.MAX_VALUE). This is not yet
possible in PHP and so you must use the JavaClass. A
script creates an instance of JavaClass and uses that
to call static methods and to access static fields. This is unusual because it
requires a developer to create an instance of an object just to access
non-instance (static) methods and fields!
The sample code earlier showed how to iterate over a Java collection. This is
fairly long-winded and less expressive than a PHP
foreach statement. At the moment,
the Java Bridge does not integrate Java iterators and PHP
foreach statements. The following code
shows how to use Java iterators in PHP:
$iterator = $list->iterator();
while ($iterator->hasNext() == TRUE) {
var_dump($iterator->next()); echo "<br/>";
}
|
Putting it all together in a real world example
The next section pulls together the previous sections to describe a real world use of the Java Bridge. The example creates a simple search engine written in PHP that can index and search files using Apache Lucene. Apache Lucene is a high-performance, full-featured text search engine library written entirely in Java. It is suitable for nearly any application that requires full-text search, especially cross-platform. For more information, see the Apache Lucene site.
The first step is to get Lucene. We are not going to use the most recent version of Lucene (although it will work perfectly well) because we want to make comparisons with the PHP implementation of Lucene, which is based on Lucene 2.2.0.
- Download
lucene-2.2.0.tar.gz.For example, from the following mirror:http://mirror.cc.columbia.edu/pub/software/apache/lucene/java/archive/. - Unzip the file (or run tar -xvzf lucene-2.2.0.tar.gz).
- Find the two JAR files, lucene-core-2.2.0.jar and lucene-demos-2.2.0.jar.
The next step is to write a PHP script that creates a Lucene search index:
- In the Java perspective, create a new application by selecting
File -> New -> Other. Select WebSphere Smash PHP
Application and call it
Lucene. - Right-click on the public folder and select New -> File.
- Give your file the name of
index.phpand click Finish. - Copy the two Lucene JAR files from earlier into the Lucene/lib directory.
- To make sure that WebSphere sMash uses the Lucene Java libraries, right-click on the project name, Lucene, and select WebSphere sMash Tools -> Resolve.
- Add the following code into the file:
<html> <head> <title>Search Index</title> </head> <body> <form name="input" action="/index.php" method="POST"> <label for="directory">Directory:</label> <input type="text" name="directory"> <label for="extension">File Extension:</label> <input type="text" name="extension"> <input type="submit" name="action" value="Index!"> </form> </body> </html> - Run the application by right-clicking on the project name Lucene and
selecting WebSphere sMash Application -> Run. Point the Web
browser at the local server, such as
http://localhost:8080/index.php. It looks similar to Figure 8.
Figure 8. Selecting a directory and file extension page
- Do not try and index anything yet because there is more code to add. Eventually when the form is submitted, the PHP script will create a Lucene search index and populate it with all the files in the directory that have a matching extension. It will also recurse down from the starting directory, adding files as it goes.
- Now add the following PHP code into
index.php:<?php $directory = dirname(__FILE__)."/../index"; if (file_exists($directory) === FALSE) { mkdir($directory); } define("INDEX_DIRECTORY", $directory); try { $extension = zget('/request/params/extension'); if (strlen($extension) > 0) { $directory = zget('/request/params/directory'); if (strlen($directory) > 0) { index_directory($directory, $extension); } } } catch (JavaException $exception) { echo "Index creation failed [". $exception->getMessage()."]</br>"; } ?> - Do not run it yet because it is not finished! The code gets the form variables
from the Global Context and checks whether they have been filled out. If they
have been filled out, it calls the
index_directoryfunction. This function is explained next and is responsible for adding any matching files into the Lucene search index. - Now add the following PHP code into
index.php:/** * This creates an index from scratch and adds all the documents * by recursing from the directory passed in. It also checks * each candidate file to see if it matches the file extension. */ function index_directory($path, $extension) { echo "Indexing! [".$path.",".$extension."]</br>"; // Uses the SimpleAnalyzer because we will do a performance comparison with the PHP // implementation of Lucene in the Zend Framework and it is the closest match $analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer"); $policy = new Java("org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy"); $file = new Java("java.io.File", INDEX_DIRECTORY, FALSE); $file_directory = new JavaClass("org.apache.lucene.store.FSDirectory"); $directory = $file_directory->getDirectory($file); $writer = new Java("org.apache.lucene.index.IndexWriter", $directory, TRUE, $analyser, TRUE, $policy); $writer->setUseCompoundFile(FALSE); // Insert some calls to microtime() for comparison $start_time = get_microtime(); recursive_index_directory($writer, $path, $extension); $count = $writer->docCount(); // Lucene only matches the first 10,000 tokens by default $writer->setMaxFieldLength(1000000); $end_index_time = get_microtime(); $writer->optimize(); $end_time = get_microtime(); $writer->close(); echo "Finished indexing [".$count." documents]</br>"; $t1 = $end_index_time - $start_time; $t2 = $end_time - $end_index_time; echo "Time to index = $t1 </br>"; echo "Time to optimize = $t2 </br>"; }
Explaining the details of the Java Lucene API is beyond the scope of this article. In a nutshell, the code is creating anIndexWriterobject. This is the key indexing object to which files will be added as the script recurses through directories. Note that you can back indexing by many different sources, for example, a RAM disk. In this example, the files are being read from a regular file system and so it uses theFSDirectoryclass.
Once theIndexWriteris set up, the script callsrecursive_index_directoryto actually do the indexing. This function is passed theIndexWriter, which is the directory to start from and the file extension to match candidate files against.
The following section of code completes the indexing script. Most of this code is general purpose PHP script that enumerates all the files in a directory and processes each in turn. Once it determines a file to be indexed, it creates aFileDocument. This is set up with the fully-qualified path to the file, and then adds it to theIndexWriter./** * Processes a file by adding it to the indexer. */ function index_file($writer, $path) { echo "Indexing file [".$path."]</br>"; try { // A few of the files we indexed in the examples have non // UTF-8 characters so we just skip indexing those files! $file = new Java("java.io.File", $path, FALSE); $file_document = new JavaClass("org.apache.lucene.demo.FileDocument"); $document = $file_document->Document($file); $writer->addDocument($document); } catch (JavaException $exception) { echo "Invalid characters in file!\n"; } } function get_microtime(){ list($part_one,$part_two) = explode(' ',microtime()); return ((float) $part_one + (float) $part_two); } /** * Indexes all matching files (by extension) in the directory tree. */ function recursive_index_directory($writer, $path, $extension) { echo "Indexing directory [".$path."]</br>"; // Remove any trailing slash first if (substr($path, -1) == '/') { $path = substr($path, 0, -1); } // Make sure the directory is valid if (is_dir($path) == TRUE) { if (is_readable($path) == TRUE) { $handle = opendir($path); // Scan through the directory contents $extension_length = strlen($extension); while (FALSE !== ($item = readdir($handle))) { if ($item != '.') { if ($item != '..') { $index_path = ($path.'/'.$item); if (is_dir($index_path) == TRUE) { recursive_index_directory( $writer, $index_path, $extension); } else { $position = strpos(strtolower($index_path), $extension); // Very rough and ready way to check for trailing extension! if ($position == (strlen($index_path)-$extension_length)) { index_file($writer, $index_path, $extension); } } } } } closedir($handle); } } return TRUE; } - Point the Web browser at the script and fill out the form variables as shown
in Figure 9.
Figure 9. Web browser output from indexing a directory
- Click Index! and the script indexes the files selected. In the example
above, the script pointed to some C source code and it indexed five source
files. If you refresh your Eclipse project, you have a new directory called
Index. This directory contains the search index files produced by the
Lucene search engine as shown in Figure 10.
Figure 10. Directory structure of a WebSphere sMash application
Adding search queries to the application
The final step is to write a form that allows a user to run searches against the index:
- Right-click on the public folder and select New -> File.
- Give your file the name of
search.phpand click Finish. - Add the following code into the file:
<html> <head> <title>Query</title> </head> <body> <form name="input" action="/search.php" method="POST"> <label for="query">Search Query:</label> <input type="text" name="query"> <input type="submit" name="action" value="Search!"> </form> </body> </html> - Run this script and the Web browser looks like Figure 11.
Figure 11. Search query page
- Now add the following PHP code into
search.php:<?php /** * This runs a search through an index already created. */ function search_index($path, $query) { echo "Searching for [".$query."]</br>"; $file = new Java("java.io.File", $path, FALSE); $file_directory = new JavaClass("org.apache.lucene.store.FSDirectory"); $directory = $file_directory->getDirectory($file); $searcher = new Java("org.apache.lucene.search.IndexSearcher", $directory); $analyser = new Java("org.apache.lucene.analysis.SimpleAnalyzer"); $parser = new Java("org.apache.lucene.queryParser.QueryParser", "contents", $analyser); $parsed_query = $parser->parse($query); $hits = $searcher->search($parsed_query); $count = $hits->length(); for ($index = 0; $index < $count; $index++) { $document = $hits->doc($index); echo $index.") ".$document->get("path")."</br>"; } echo "</br>Finished searching [".$count." hits]</br>"; } try { $directory = dirname(__FILE__)."/../index"; define("INDEX_DIRECTORY", $directory); $query = zget('/request/params/query'); if (strlen($query) > 0) { search_index($directory, $query); } } catch (JavaException $exception) { echo "Index search failed [".$exception->getMessage()."]</br>"; } ?>
As before, this script makes use of several Lucene classes. The essence of the script is that, instead of using theIndexWriterclass likeindex.php, it uses anIndexSearcher. This is configured with the same directory where the index files were created earlier. The string that was entered by the user in that form is then used to create a query object. The LuceneQueryParserprovides an easy way to parse query strings.
With the query parsed, the script is ready to run the search on theIndexSearcher. This returns a list of hits, which the script enumerates, displaying the path for each item. - Point a Web browser at the
search.phpand enter some search terms as shown in Figure 12.
Figure 12. Web browser output from running a search query
In this example, five hits were found matching the keywords "TSRM" and "int". Lucene has a powerful query syntax that can support a wide variety of search terms. More information about the possible search queries is available from the Apache Lucene site.
If you were looking carefully at the source code that we added to
index.php, you may have
noticed some calls to microtime and a few comments,
which indicated that we would want to check the performance.
The checks that we performed are simple timing checks. We were interested in comparing the time it takes to create an index using three different pieces of software:
- The Java implementation of Lucene called via the WebSphere sMash Java Bridge.
- Java Lucene called from a Java application.
- The PHP implementation of Lucene in the Zend Framework.
To make this a fair comparison, we used Lucene Version 2.2.0, which is what the
Zend implementation is based on. We also used the Lucene
SimpleAnalyser. A detailed discussion of the Zend
implementation is beyond the scope of this article. However, it is a faithful port
of the Lucene code and it generates indexes that have identical format to those
generated by the Java version.
The performance comparison was to index all of the PHP test scripts (*.phpt files) under the PHP 5.3 source tree. The times taken to create and optimize the index are shown in Table 2.
Table 2. Performance comparison for Lucene search
| Technology | Time in seconds |
| WebSpere sMash Java Bridge | 9 |
| Java Lucene | 8 |
| Zend Search Lucene | 200 |
This is gives a quick idea of how timings compare using these technologies "out-of-the- box". The Java JIT is switched on in these timings, and in an application like Lucene, it makes a considerable difference in execution times.
Neither of these reasons is taken as a reason not to use the Zend implementation. In fact, if you are not using Java and your principle development language is PHP, using a search engine that is also written in PHP has many advantages. Considerations such as understanding and modifying the code easily may outweigh those of raw performance.
What is more interesting is the comparison between using PHP and the Java Bridge and using a Java application. The fact that the timings are close tells us that we are not wasting too much time in the Java Bridge, or in fact, in running PHP on the Java VM.
There are, of course, other PHP to Java Bridges. For example, there is a commercial implementation in the Zend Platform and an open source implementation available from sourceforge.net. While we have not used either of these implementations, the fact that they exist lend support to the argument for using Java for what it is good for (algorithmic performance) and taking advantage of the fact that PHP is easy to use.
If you repeat these experiments, you may notice slight differences in the indexes that are created. One of the useful features of the Zend implementation is that it creates indexes of exactly the same format as the Java implementation, which means that you can check them with standard Java tools (for example, Luke, which you can download from the Luke site). These differences are all relatively easy to explain and do not affect the timing comparison. For example, there are slight differences between the PHP and Java analyzers.
In this article, you:
- Created an application in PHP and WebSphere sMash.
- Used the Java Bridge to create and invoke Java objects.
- Explored using Java collections from PHP scripts.
- Learned how the Java Bridge does type coercion and exception handling.
- Developed a search engine based on the Java Lucene libraries.
- Looked at the performance of the Java Lucene libraries.
Now that you have completed this article, you can expand your use of Java libraries and PHP scripting. Why not combine some more Java libraries with PHP in WebSphere sMash? Let us how you know how you are doing in the Project Zero forums. If you want to learn more about the Zero Global Context and other relevant topics, see the WebSphere sMash Developer's Guide listed in the Resources section below.
The authors would like to thank Naveen Noel Jakkamsetti at ZSL Inc. for his help with this article.
Learn
- Read
Get started with Project Zero, WebSphere sMash, and PHP
to download WebSphere sMash and to create a PHP application.
- If you'd like to join the Project Zero community or just keep up with what's
happening, visit the Project Zero Web site, where you'll
find blogs, forums, and a community of fellow developers.
- Refer to
WebSphere sMash documentation
for all the documents associated with this project, including FAQs, tutorials, and demos.
- Listen to a
podcast interview and discussion
with IBM Fellow and WebSphere CTO Jerry Cuomo, who provides his insight into
WebSphere sMash and its impact on Web 2.0 development.
- Check out the
developerWorks PHP zone
for comprehensive PHP project resources.
- The
developerWorks Web development zone
includes resources for Web developers using Web 2.0 technologies.
- The
developerWorks Ajax resource center
is packed with content and tools to get you started developing Ajax applications
today.
Get products and technologies
- You can find full instructions for downloading and installing WebSphere sMash for
PHP at the Project Zero site.
Discuss
- Participate in the
Project Zero discussion forum.

Ant Phillips is a Software Developer in IBM's Java Technology Centre in Hursley, United Kingdom. His current focus is on WebSphere sMash, a simple environment for creating dynamic Web applications. Before joining IBM, Ant was the technical lead at an innovative startup based in Newbury, UK. In previous lives, Ant worked for Sony® and Microsoft® and thoroughly enjoyed visiting Tokyo, Seattle, and several places in between. In his spare time, he plays as much sport as his wife and two children let him get away with.

Zoe Slattery is a Software Engineer working for IBM Hursley, United Kingdom. Her current interests are in Open Source development and PHP. In the past, Zoe has worked in many areas of the IT industry. She started as a FORTRAN programmer, worked on parallel systems for numerically intensive computation, and managed a data archive for the European Centre for Medium Range Weather Forecasts. More recently, she has managed development teams working on IBM's Java Virtual Machine and on Open Source implementations of the Java class libraries.
Comments (Undergoing maintenance)





