Just got this installed, and when I try to export out of Case Tracker, I get

Fatal error: Class 'org_apache_poi_hssf_usermodel_HSSFWorkbook' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 18

I checked the folder is referencing and sheetnode_xls.export.inc is there.

Comments

infojunkie’s picture

Please go to the status report page (admin/reports/status). What are the values for "Apache POI", "PHP/Java Bridge extension", "PHP/Java Bridge include files" ?

In case the PHP/Java Bridge entries are fine but Apache POI isn't, please make sure the Sheetnode XLS settings (admin/settings/xls) is configured to point to a folder where you extracted the Apache POI 3.5-FINAL package. check the status report page again.

spivey’s picture

All this checks out -
Apache POI - is found.
PHP/Java Bridge Extension - is loaded in php.ini
PHP/Java Bridge Include Files - are found.

I tried moving the POI folders from a folder on /usr to the sites/defaults/files directory in case there was a file permission error, but that didn't help. I've also tried POI- 3.5 Beta6 to see if the 3.5 Final had an issue, but that wasn't it (I couldn't find 3.4 Beta).

The PHP/Java Bridge files a sym'd to my tomcat folder, but that shouldn't matter, should it?

infojunkie’s picture

> The PHP/Java Bridge files a sym'd to my tomcat folder, but that shouldn't matter, should it?
Not that I know of, and besides the fact that the status report shows the POI version number (it does show it, doesn't it?) means that the module is able to call the POI jar file and get a reply.

What are you PHP/Java Extension settings (in php.ini)? Mine are:

extension = java.so
java.log_level = 3
java.log_file = /var/log/php-java-bridge.log
java.security_policy = Off

Please examine the error log above to see if anything's reported.

spivey’s picture

From php.ini:

[java]
java.java_home = "/usr/lib/java/bin"
java.java = "/usr/lib/java/jre"
java.class.path = "/usr/local/tomcat6/webapps/JavaBridge/WEB-INF/lib/JavaBridge.jar"
java.library = "/usr/local/javabridge/ext/java"
java.library.path = "/usr/local/javabridge/ext"
java.log_file = "/var/log/php-java-bridge.log"
java.log_level = 3
java.security_policy = Off

I didn't have a log file specified or the security policy declared, so I added both. After restarting Apache and Tomcat I still get the same error when trying to export a XLS, and nothing is recorded in the logfile. Does this sound like the request isn't making it to java?

spivey’s picture

Yes, the POI version is shown on the status report page.

infojunkie’s picture

Debugging PHP/Java Bridge issues can be a pain. Thanks for your patience.

Please try the following code, outside of Drupal: http://php-java-bridge.sourceforge.net/pjb/examples/source.php?source=ex.... You will need to make POI available in some shared JAR folder on Tomcat. Does this code generate a file?

When this code works, please replace the line in sheetnode_xls.export.inc

$workbook = $type == 'xls' ? new org_apache_poi_hssf_usermodel_HSSFWorkbook() : new org_apache_poi_xssf_usermodel_XSSFWorkbook();

with

$workbook = $type == 'xls' ? new java("org.apache.poi.hssf.usermodel.HSSFWorkbook") : new java("org.apache.poi.xssf.usermodel.XSSFWorkbook");

Does this line still give you an error?

spivey’s picture

The sample in Tomcat worked, I downloaded an excel file that looks exactly like the one on the JavaBridge examples page - is this correct?

When replace the code in sheetnode_xls.export.inc, I no longer get the error but nothing downloads - it's just a blank page - the URL is the node path assigned to the feed.

spivey’s picture

Just to make sure all the basis were covered, I cleared Drupal's cache, View's cache, my browsers cache, and restarted Tomcat and Apache. Now when I try sheetnode I get:

Fatal error: Class 'org_apache_poi_ss_usermodel_Cell' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 97

infojunkie’s picture

> The sample in Tomcat worked, I downloaded an excel file that looks exactly like the one on the JavaBridge examples page - is this correct?
Yes I wanted to make sure that the standard examples do work.

> When replace the code in sheetnode_xls.export.inc, I no longer get the error but nothing downloads - it's just a blank page - the URL is the node path assigned to the feed.
That's expected. I was trying to confirm the source of the problem, but it's not solved yet.

The PHP class "org_apache_poi_hssf_usermodel_HSSFWorkbook" is supposed to be auto-created by the bridge along with all other classes contained in the jars specified in java_autoload() - used in sheetnode_xls.export.inc line 7. That's the class that's not found, whereas the Java class itself, when accessed via java("org.apache.poi.hssf.usermodel.HSSFWorkbook"), is found.

To determine whether this is a missing setting in the Sheetnode module or something global to your PHP/Java Bridge installation, please try the following code, outside of Drupal:

<?php require_once("java/Java.inc");
java_autoload("<full path of each jar in POI folders separated by semicolon>");

header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=downloaded.xls");

// create a 50x40 excel sheet and return it to the client
$workbook = new org_apache_poi_hssf_usermodel_HSSFWorkbook();
$sheet = $workbook->createSheet("new sheet");

for(

$y=0; $y<40; $y++) {
 
$row = $sheet->createRow($y);
  for(
$x=0; $x<50; $x++) {
   
$cell = $row->createCell($x);
   
$cell->setCellValue("cell $x/$y");
  }
}

// create and return the excel sheet to the client
$memoryStream = new java_io_ByteArrayOutputStream();
$workbook->write($memoryStream);
$memoryStream->close();
echo
java_values($memoryStream->toByteArray());
?>

The only changes are the java_autoload() call and different class names. Are you able to run this?

spivey’s picture

This gives me a HTTP 500 error:

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

javax.servlet.ServletException: java.lang.RuntimeException: PHP Fatal error:  Uncaught [[o:Exception]:"java.lang.Exception: Invoke failed: [[o:JavaBridge]]->updateJarLibraryPath((o:String)[o:String], (o:String)[o:String], (o:String)[o:String], (o:String)[o:String]). Cause: java.io.IOException: Could not open jar file <full path of each jar in POI folders separated by semicolon>, reason: error in opening zip file VM: 1.6.0_12@http://java.sun.com/" at:
#-18 php.java.bridge.DynamicJavaBridgeClassLoader.checkJarFile(DynamicJavaBridgeClassLoader.java:108)
#-17 php.java.bridge.JarLibraryPath.createUrls(JarLibraryPath.java:192)
#-16 php.java.bridge.JarLibraryPath.checkURLs(JarLibraryPath.java:117)
#-15 php.java.bridge.JarLibraryPath.<init>(JarLibraryPath.java:64)
#-14 php.java.bridge.DynamicJavaBridgeClassLoader.checkJarLibraryPath(DynamicJavaBridgeClassLoader.java:74)
#-13 php.java.bridge.DynamicJavaBridgeClassLoader.updateJarLibraryPath(DynamicJavaBridgeClassLoader.java:87)
#-12 php.java.bridge.StandaloneJavaBridgeClassLoader.updateJarLibraryPath(StandaloneJavaBridgeClassLoader.java:98)
#-1 in /usr/local/tomcat6/webapps/JavaBridge/java/Java.inc on line 109

php.java.servlet.PhpCGIServlet.handle(PhpCGIServlet.java:451)
php.java.servlet.CGIServlet.doGet(CGIServlet.java:474)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

root cause

java.lang.RuntimeException: PHP Fatal error:  Uncaught [[o:Exception]:"java.lang.Exception: Invoke failed: [[o:JavaBridge]]->updateJarLibraryPath((o:String)[o:String], (o:String)[o:String], (o:String)[o:String], (o:String)[o:String]). Cause: java.io.IOException: Could not open jar file <full path of each jar in POI folders separated by semicolon>, reason: error in opening zip file VM: 1.6.0_12@http://java.sun.com/" at:
#-18 php.java.bridge.DynamicJavaBridgeClassLoader.checkJarFile(DynamicJavaBridgeClassLoader.java:108)
#-17 php.java.bridge.JarLibraryPath.createUrls(JarLibraryPath.java:192)
#-16 php.java.bridge.JarLibraryPath.checkURLs(JarLibraryPath.java:117)
#-15 php.java.bridge.JarLibraryPath.<init>(JarLibraryPath.java:64)
#-14 php.java.bridge.DynamicJavaBridgeClassLoader.checkJarLibraryPath(DynamicJavaBridgeClassLoader.java:74)
#-13 php.java.bridge.DynamicJavaBridgeClassLoader.updateJarLibraryPath(DynamicJavaBridgeClassLoader.java:87)
#-12 php.java.bridge.StandaloneJavaBridgeClassLoader.updateJarLibraryPath(StandaloneJavaBridgeClassLoader.java:98)
#-1 in /usr/local/tomcat6/webapps/JavaBridge/java/Java.inc on line 109

php.java.servlet.fastcgi.FastCGIServlet$CGIRunner.parseBody(FastCGIServlet.java:497)
php.java.servlet.fastcgi.FastCGIServlet$CGIRunner.doExecute(FastCGIServlet.java:396)
php.java.servlet.fastcgi.FastCGIServlet$CGIRunner.execute(FastCGIServlet.java:404)
php.java.servlet.CGIServlet.handle(CGIServlet.java:401)
php.java.servlet.PhpCGIServlet.handle(PhpCGIServlet.java:427)
php.java.servlet.CGIServlet.doGet(CGIServlet.java:474)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

note The full stack trace of the root cause is available in the Apache Tomcat/6.0.20 logs.

Something's wrong with Java?

infojunkie’s picture

Sorry, I should have been more explicit: the string "<full path of each jar in POI folders separated by semicolon>" in the code snippet needs to be replaced with something like:

"/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/lib/commons-logging-1.1.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/lib/junit-3.8.1.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/lib/log4j-1.2.13.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/ooxml-lib/ooxml-schemas-1.0.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/ooxml-lib/dom4j-1.6.1.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/ooxml-lib/xmlbeans-2.3.0.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/ooxml-lib/geronimo-stax-api_1.0_spec-1.0.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/poi-contrib-3.5-FINAL-20090928.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/poi-scratchpad-3.5-FINAL-20090928.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/poi-ooxml-3.5-FINAL-20090928.jar;/var/www/d6/sites/all/modules/sheetnode/poi-3.5-FINAL/poi-3.5-FINAL-20090928.jar"

which is the full path of *each* jar file inside the POI folder. Just replace the path here with your own.

spivey’s picture

I'm going to try this, but I've ran into other issues (not with this) that need to be addressed. I'll get an update to you as soon as I can.

spivey’s picture

Ok I'm back. Long story short I had something else to take care on this box, but now that's fixed.

Since my last post I went back and was re-reading some docs. I realized I was running Tomcat6 and decided to try 5.5,

Now that is running, but the PHP/Java Bridge module isn't finding Java.inc. I've tried links and directories in the Drupal located and on the /usr mount, but it just will not accept any location I give it. What exactly does this check when I enter a path on the PHP/Java Bridge module page?

infojunkie’s picture

Given a path, it checks if the file [path]/java/Java.inc exists. Was this running correctly on Tomcat 6?

Also, did you try my last suggestion on Tomcat 6 before switching to 5.5 ?

spivey’s picture

Ok - I fixed the code and tried the example in post 11, I received the same spreadsheet example as before.

infojunkie’s picture

That is good news: your PHP/Java Bridge installation works well, and the bug is somewhere in Sheetnode ;-)

Can you please find the value of the Drupal variables called "sheetnode_xls_jar" and "sheetnode_xls_jar_path" ? You can find them in the database, table "variable", and search for records where field "name" LIKE "%sheetnode%".

The "sheetnode_xls_jar" variable should contain the same string that you entered manually to replace "<full path of each jar in POI folders separated by semicolon>". Are they the same?

spivey’s picture

Listed first in the query results is sheetnode_xls_jar, with the path to each jar file following seperated by ';'

The next entry in the table is sheetnode_xls_jar_path, with the correct path to POI following.

This path is the same that I used in the last test code you posted.

infojunkie’s picture

I'm running out of ideas...

What is the error you now get? Anything in the PHP/Java Bridge log file or Drupal status report? Which POI version are you using?

If you'd like me to check your installation, I can do that.

spivey’s picture

I started out with:
Fatal error: Class 'org_apache_poi_hssf_usermodel_HSSFWorkbook' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 18

Changing this - new org_apache_poi_hssf_usermodel_HSSFWorkbook() : new org_apache_poi_xssf_usermodel_XSSFWorkbook
to this
new java("org.apache.poi.hssf.usermodel.HSSFWorkbook") : new java("org.apache.poi.xssf.usermodel.XSSFWorkbook")

now gives me this:
Fatal error: Class 'org_apache_poi_ss_usermodel_Cell' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 97

Nothing in the logs (apache or php/java Bridge - in fact the latter is empty). POI is 3.5 FINAL.

infojunkie’s picture

It seems that java_autoload fails to find the Java class. Here are some more tests:

* What's your PHP version?

* In sheetnode_xls.export.inc, please replace

require_once("$JAVA_BASE/java/Java.inc");

with

require_once("java/Java.inc");

Any difference?

* Edit your java/Java.inc, function java_autoload_function5, by replacing:

if(!($client->invokeMethod(0, "typeExists", array($str)))) return false;

with

//if(!($client->invokeMethod(0, "typeExists", array($str)))) return false;

i.e., commenting it out. What happens now?

spivey’s picture

PHP 5.2.6-1

require_once("java/Java.inc");
Results in:
Fatal error: require_once() [function.require]: Failed opening required 'java/Java.inc' (include_path='.:/usr/share/php:/usr/share/pear') in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 6

I roll back that change and
//if(!($client->invokeMethod(0, "typeExists", array($str)))) return false;
I still receive
Fatal error: Class 'org_apache_poi_ss_usermodel_Cell' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 97

infojunkie’s picture

2 things are inconsistent still:

* The code require_once("java/Java.inc"); works in the non-Drupal examples above, but not in the Sheetnode module.

* The autoloaded class org_apache_poi_hssf_usermodel_HSSFWorkbook is found in the non-Drupal example, while it's not found in the Sheetnode module.

If we can resolve those inconsistencies we'll solve this problem :-)

spivey’s picture

I removed the modules from Drupal and deleted to directories on the server they were in. I downloaded the tar ball again and set everything back up - but still the same error.

This question might be unrelated but - when creating the feed view it asks for a path - when I assign the path 'casetracker' I do not get an error, but I also don't get a xls file. If I assign a path of xls_export (one that doesn't already exist) I do get the error. Is this correct?

infojunkie’s picture

> This question might be unrelated but - when creating the feed view it asks for a path - when I assign the path 'casetracker' I do not get an error, but I also don't get a xls file. If I assign a path of xls_export (one that doesn't already exist) I do get the error. Is this correct?

I would think so. If the path already exists then it's probably picking up the other definition and thus the XLS exporting code is never called.

spivey’s picture

Sorry to take so long on this but that box was giving me some issues. I went ahead and loaded a fresh OS install on a different box and a new install of Drupals. I haven't loaded any other modules except for this one (and what it requires). I'm still getting the same error that I first reported :

Fatal error: Class 'org_apache_poi_hssf_usermodel_HSSFWorkbook' not found in /var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc on line 18

So I go in and change
org_apache_poi_hssf_usermodel_HSSFWorkbook
to
org.apache.poi.xssf.usermodel.XSSFWorkbook
as you suggested in post #6, and still get the other error that references line 97.

spivey’s picture

I've been hacking around at this and managed to get a spreadsheet download - but here is the contents:

    Fatal error: Uncaught [[o:Exception]:"java.lang.Exception: CreateInstance failed: new . Cause: java.lang.ClassNotFoundException: VM: 1.6.0_12@http://java.sun.com/" at:
    #-8 java.lang.Class.forName0(Native Method)
    #-7 java.lang.Class.forName(Class.java:247)
    #-6 php.java.bridge.StandaloneJavaBridgeClassLoader.forName(StandaloneJavaBridgeClassLoader.java:137)
    #-5 php.java.bridge.JavaBridge.CreateObject(JavaBridge.java:449)
    #-4 php.java.bridge.Request.handleRequest(Request.java:453)
    #-3 php.java.bridge.Request.handleRequests(Request.java:491)
    #-2 php.java.bridge.http.ContextRunner.run(ContextRunner.java:145)
    #-1 php.java.bridge.ThreadPool$Delegate.run(ThreadPool.java:60)
    #0 /opt/tomcat/webapps/JavaBridge/java/Java.inc(128): java_ThrowExceptionProxyFactory->getProxy(28, 'org.apache.poi....', 'T', false)
    #1 /opt/tomcat/webapps/JavaBridge/java/Java.inc(211): java_Arg->getResult(false)
    #2 /opt/tomcat/webapps/JavaBridge/java/Java.inc(212): java_Client->getWrappedResult(false)
    #3 /opt/tomcat/webapps/JavaBridge/java/Java.inc(312): j in /opt/tomcat/webapps/JavaBridge/java/Java.inc on line 109

Have I gone a step forward, or 2 back?

infojunkie’s picture

The exception report you sent does not contain the class name. Specifically, the line

#0 /opt/tomcat/webapps/JavaBridge/java/Java.inc(128): java_ThrowExceptionProxyFactory->getProxy(28, 'org.apache.poi....', 'T', false)

should contain the class name, but it's shortened. Can you find the full version?

Also, you can send your modified file here for me to look at it. I might spot obvious defects.

spivey’s picture

StatusFileSize
new12.67 KB

Can you find the full version?

Where? Is there another log file I can look in?

I've attached sheetnode_xls.export.inc - I added the .info just to upload here.

spivey’s picture

StatusFileSize
new11.36 KB

Found this log from tomcat

infojunkie’s picture

Your translation of

org_apache_poi_hssf_usermodel_HSSFWorkbook

to

org.apache.poi.xssf.usermodel.HSSFWorkbook

is incorrect. Instead, you need to change

org_apache_poi_hssf_usermodel_HSSFWorkbook

to

java("org.apache.poi.xssf.usermodel.HSSFWorkbook")
infojunkie’s picture

Can I access your site and/or server to see for myself? I might find something obvious...

spivey’s picture

org.apache.poi.hssf.usermodel.HSSFWorkbook
Which instance? The only one I could find to change was:
$workbook = $type == 'xls' ? new java("org.apache.poi.hssf.usermodel.HSSFWorkbook") : new java("org.apache.poi.xssf.usermodel.HSSFWorkbook");
That is how it now reads.

The site is still internal, so there isn't any outside access right now.

/edit:Update - with that code changed I still get the same error.

infojunkie’s picture

I meant all instances of such classes. I will send you a file within a few hours with the proper changes.

infojunkie’s picture

StatusFileSize
new12.62 KB

Attached is a version of sheetnode_xls.export.inc that I tested successfully. Please let me know if it works for you.

spivey’s picture

Using that I'm getting a spreadsheet with the contents:

<b>Fatal error</b>:  Class 'java_io_ByteArrayOutputStream' not found in <b>/var/www/aqcomm/addmods/sheetnode/sheetnode_xls.export.inc</b> on line <b>80</b><br />

infojunkie’s picture

StatusFileSize
new12.62 KB

Try this one please :-)

spivey’s picture

Hey hey!! That did it!

What did you have to change?

infojunkie’s picture

Phew! I changed every Java class occurrence from the form x_y_z_ClassName (that relied on java_autoload() working correctly) to java('x.y.z.ClassName').

I don't want to close this issue though, because java_autoload *should* be working correctly. I will post a message on the PHP/Java Bridge mailing list to ask about cases where java_autoload might fail. Otherwise, all other Java-related files (XLS import, ODS export) would need the same treatment :-(

Thanks a lot for your patience with this issue.

infojunkie’s picture

Title:Fatal Error» Fatal Error: Java class not found using java_autoload()
infojunkie’s picture

I see you contacted PJB already! I just subscribed to this mailing list, so can you please reply to your own message so that I can join that thread (instead of starting a new one)?

infojunkie’s picture

To summarize the response made on the PJB mailing list: it seems you're using PJB 5.5.4 where they removed support for classes declared as x_y_z_ClassName. They will revert that change in the next release 5.5.4.1. I suggest that we resume this thread once this new version is released.

On my side, I will make sure that my module no longer depends on the presence of the java.so extension, or php.ini settings.

ranvir.prasad’s picture

Subscribing.
Does the new version not rely on java.so or java_dll extension? PHP/Javabridge site points out that servlet model is better than using java.so or java_dll part.

infojunkie’s picture

@ranvir: I haven't yet updated the module to not depend on java.so/dll. I will make a new release once this is implemented and tested. In the meantime, you can modify the code in php_java_bridge.install, function php_java_bridge_requirements, by replacing the line

$extension = extension_loaded('java');

with

$extension = TRUE;

All remaining settings should be the same as described on the module page.

spivey’s picture

Just to let you know that wasn't me that started the thread at PJB; but it is a little reassuring I wasn't the only one having the same problem.

Thanks for your work on this.

nsoules1’s picture

Component:Code» Core

Hi All,

I'm the person that posted the thread on PJB. I listened to Peter's suggestion, which is what Kratrib has passed along here:

// $extension = extension_loaded('java');
$extension = TRUE;

This really helped, and I was able to install the php-java-bridge module and then the xls sheetnode module. Now, however, I'm running into some errors when trying to import an xls file:

"java.lang.Exception: Invoke failed: [[c:WorkbookFactory]]->create((o:InputStream)[o:FileInputStream]). Cause: java.lang.NoSuchMethodError: org.apache.poi.poifs.filesystem.POIFSFileSystem.hasPOIFSHeader(Ljava/io/InputStream;)Z VM: 1.6.0_16@http://java.sun.com/" at: #-10 org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:59)

I'm thinking that this may be related to this thread, so I tried this:

// $workbook = org_apache_poi_ss_usermodel_WorkbookFactory::type()->create($inp);

$WbClass = new java_class('org.apache.poi.ss.usermodel.WorkbookFactory');
$workbook = $WbClass->create($inp);

Didn't seem to make a difference... however I did not change how $inp was getting initialized (I don't see an error associated with it). Not sure how to pass in a param w/ single quotes using that syntax.

Also, it may be worth noting that I cannot get the php and jsp samples for PhpJavaBridge to work at the same time. I have an Apache-Tomcat connector, and when I enable this for JavaBridge, the jsp examples work, but the php examples do not. If I have it disabled, the php examples work, but jsp examples don't. Is that normal? Will the JavaBridge still function/function well enough for sheetnode to work? (Sorry if that's more of a PhpJavaBridge question)

Any help would be appreciated. Thanks!

infojunkie’s picture

@nsoules1, please open a separate issue because this seems to have nothing to do with the extension problem.

nsoules1’s picture

Kratib - ok, opened a new one.

infojunkie’s picture

Component:Core» XLS integration
infojunkie’s picture

Version:6.x-1.2» 6.x-1.5-beta3
Status:Active» Closed (fixed)

The latest 1.5-beta3 version has removed the Java dependencies and replaced them with PHPExcel library. Please delete your sheetnode folder and install the latest version as per the instructions on the module page.