ShowTable of Contents
Overview
IBM® Lotus® Quickr® is team collaboration software that can help you access and interact with the people, information, and project materials you need to get your work done. The Quickr product is available for two servers, IBM Lotus Domino® server and IBM WebSphere® Portal. Here we use Lotus Quickr on WebSphere Portal as a content repository to store the information in documents. For more information Lotus Quickr refer to the
Quickr for WebSphere Portal IBM Support page.
In our current project, we use Lotus Quickr as a content repository tool that stores all the sensitive project-related documents in its libraries. Almost every day Lotus Quickr is loaded with new data, thus increasing the space consumption for the Quickr repository in the server.
So it's important to maintain Quickr's performance by removing the unwanted data from it, which is how the development work for this “create utility”script was started.
Using IBM Eclipse for development
You can use Lotus Quickr Web services with any Java
TM Development Kit 1.4 to develop this cleanup utility script. To know more about using Quickr Web services, refer to the developerWorks article, “
Introducing IBM Lotus Quickr Web services.”
Recursive traversal
When Lotus Quickr is used as a repository we can perform this cleanup as a maintenance task to remove unwanted documents / unused folders, helping to improve its performance. As Lotus Quickr stores the documents in a tree hierarchy structure, the efficient way of traversing through the folders in the library and deleting the same is by using “recursive traversal.”
Scenario: For example, if Folder1 has an empty folder named “Folder2”, which in turn has an empty “Folder3”, then the order of deletion would be Folder3, then Folder2, and then Folder1.
Logic: Traverse through the Quickr repository until the end node (Folder3) to find the empty folder/child folders, and then the code must transverse back to Folder1 to delete the folders. Here, both the forward traversal and reverse traversal are necessary to delete a particular folder.
Quickr Web services provides the following:
- Library Service. Retrieves a list of top-level libraries.
- Document Service. Retrieves subfolders, documents, and metadata.
- Content Service. Retrieves the server information.
For more details, refer to the above-cited developerWorks article, “
Introducing IBM Lotus Quickr Web services.”
In addition, if you happen to delete a necessary empty folder structure by using this cleanup utility, you can recreate the same (deleted) folder structure, using the recovery utility. This script is explained at the end of the article and is included with this restore utility.
Implementation
The way to query Lotus Quickr using Quickr Web services is by calling the Quickr content service URL:
String url =<Quickr URL>:<portnumber>
String endpoint = url + "/dm/services/ContentService";
ContentService_ServiceLocator con_ser = new ContentService_ServiceLocator();
con_ser.setContentServiceEndpointAddress(endpoint);
Depending on the type of information to be retrieved, the Library/Document/Content service object is created, and authentication must be done for each service type by passing the userid and password. In listing 1 you can see the service object, returned from the getLibraryService() method, which in turn calls the getLibraries() method.
Listing 1. Code to get all libraries in the Quickr repository
LibraryService_ServiceLocator locator1 = new LibraryService_ServiceLocator();
ClbCategoryType[] category = new ClbCategoryType[10];
category[0]=new ClbCategoryType("DocumentManager");
category[1]=new ClbCategoryType("PersonalDocumentManager");
LibraryService_PortType service1 = locator1.getLibraryService();
((Stub) service1)._setProperty(Call.USERNAME_PROPERTY, "<userid>");
((Stub) service1)._setProperty(Call.PASSWORD_PROPERTY, "<password>");
ClbLibrariesResponse resp1 = service1.getBusinessComponents(null, null, category);
if(resp1 != null)
ClbLibrary[] libraries=resp1.getLibraries();
To access the document and perform operations on it, we can use the Document service object of the type DocumentService_PortType, derived from getDocumentService (). Listing 2 shows the operation to be performed on the documents in the Quickr library using the Document service object.
Listing 2. Code to delete empty folders in a Quickr library
public ArrayList getFolderreverse_Recursive(DocumentService_PortType service2
,String folderID,String folderLabel,BufferedWriter runwriter2,ArrayList emptyFolders_libry)
{
try{
String newfolderid=null;
String foldername="";
String path="";
String folderid="";
// locator for folders
ClbFoldersResponse resp2 = service2.getFolders(folderID, null);
ClbFolder[] folders =resp2.getFolders();
if (folders!=null && folders.length > 0)
{
for (int i=(folders.length-1);i>=0;i=i-1)
{
foldername=folders[i].getLabel();
path=folders[i].getPath();
folderid=folders[i].getId();
path=path.substring(path.indexOf("/clb:libraries/")+14,path.length());
<to excludefolder>
// if( (!(foldername.toLowerCase().contains("Excludefolder1")))
//{
//if((!(path.contains("Excludefolder11"))))
//{
if(isFolderhasFolders(service2,folderid,foldername)) {
getFolderreverse_Recursive(service2,folderid,foldername,runwriter2,emptyFolders_libry);
}
else if(!isFolderhasdoc(service2,folderid,foldername)){
emptyFolders_libry.add(path); deleteFolder(service2,folderid);
runwriter2.write(foldername+" :"+path +" is empty deleting...");
runwriter2.newLine();
}
<to excludefolder>//}
//}
}
}
// check parent folder empty
boolean parentPresent=isFolderEmpty(service2,folderID,folderLabel);
if(parentPresent)
{
deleteFolder(service2,folderID);
runwriter2.write(foldername+" :"+path +" is empty deleting...");
}
}
catch(Exception e )
{
e.printStackTrace();
}
return emptyFolders_libry;
}
Here is the code to have the Document service get the folder and sub-folder details:
DocumentService_ServiceLocator locator2 = new DocumentService_ServiceLocator();
DocumentService_PortType service2;
service2 = locator2.getDocumentService();
The isFolderhasFolders() method implementation shown in listing 3 below checks the existence of folders for the passed folder / parent folderid we are passing, where DocumentService_PortType is an interface in the package com.ibm.content.clb.webservices.
Listing 3. Implementation of isFolderhasFolders
public static boolean isFolderhasFolders(DocumentService_PortType
service2,String libryfolder_id,String libryfolder_name)
{
boolean hasFolder=false;
ClbFolder[] folders=null;
ClbFoldersResponse resp2=null;
try
{
resp2=service2.getFolders(libryfolder_id,null);
folders =resp2.getFolders();
if(folders!=null && folders.length > 0)
{
hasFolder=true;
}
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (com.ibm.content.clb.webservices.exception.ServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return hasFolder;
}
isFolderhasdoc() -> to check the folder has document.
NOTE: The libraryid and libraryname is passed for the parameters folderID, foldername, when the method
getFolderreverse_Recursive is called for the
first time. When the method is called inside the same method (as this is a recursive method), then folderid and foldernames are passed for the parameters folderID, foldername respectively:
service2.getFolders(folderID, null); -- > returns the list of folders.
isFolderhasFolders() -> does check whether the folder is empty.
Listing 4 is the code to check whether there are documents existing in a folder or whether the folder is empty. When the folder is deleted, its name and the path are logged to a file for recording purposes, which can also be used as an input if it needs to be recovered/recreated.
Listing 4. Code to check whether documents exist
res=service2.getDocuments(libryfolder_id, null, null);
docments=res.getDocuments();
if(docments !=null && docments.length>0)
docPresent=true;
service2.deleteFolder(folderid,null) -> delete folder using folderid.
Here is the code snippet for writing the Empty folder name and the path to the log file:
runwriter2.write(foldername+" :"+path +" is empty deleting...");
For the complete code, refer to the source code in the Sourcecode_QuickrCleanupUtility.zip file attached to this article.
NOTE: If you don't want this deletion-of-empty-folders script to be performed for a particular folder named, say, “Excludefolder1”, just uncomment the lines in the code preceded with <
to excludefolder> in listing 2 above.
Results
In our project, the script took some 5 hours to delete the empty folders structure in the 980 libraries in the Quickr repository, with roughly 50,000 empty folders deleted from 1000 libraries with a total repository storage size is of 80GB. Note also that the speed depends on the size of the repository; the utility will run faster for libraries of fewer documents.
Try it!
The code in the attached file Sourcecode_QuickrCleanupUtility.zip will clean up all the libraries in the Quickr repository. As this cleanup utility runs over the Quickr libraries one by one, to start with, just run the utility for a particular library in the repository. By doing just a few modifications in the given code, you'll be able to do it.
Restore utility
After running the cleanup utility, if you discover that you have inadvertently deleted a necessary empty folder structure in a particular library, there's no need to panic; you can run the restore utility script for that library.
To run the restore utility for a particular library, you simply must pass the cleanup log file entries for that library. The restore task takes the deleted folder path as an input from the log file and recreates the deleted empty folder with the same folder structure it had previously (when it was deleted by the cleanup utility), using the deleted folder path taken from the log file.
The logic behind this task is to get the folder name and folder path from the log file. The below line is for writing the Empty folder name and the path is written to the log file. You can see the line below in the QuickrAccessforDraft.java:
runwriter2.write(foldername+" :"+path +" is empty deleting...");
To recreate the deleted folder, just read the folder name and its path from the log file and create the folder using the following code:
ClbFolder newfolder = new ClbFolder();
newfolder.setPath(path);
resp3 = service2.createFolder(newfolder);
where path ->quickrpath of the folder along with its name is taken from logfile.
Results
It took 20 minutes to recreate the deleted empty folder structure of about 4000 folders, using this restore script, for a Quickr repository of 1000 libraries and total storage size of 80GB.
The download
The attachment contains the bundled source code for the cleanup and restore utilities. If you pass the command line argument as “cleanup”, then the corresponding cleanup code will be executed; likewise, to execute the restore utility, you need to pass as “restore”.
You can download the dependant files from the
Quickr product documentation. The list of dependant jar files for the Quickr Web services are as follows:
wsdl4j-1.5.1.jar
webserviceutils.jar
saaj.jar
log4j-1.2.12.jar
jaxrpc.jar,
commons-logging-1.0.4.jar
commons.-discovery-0.2.jar
axis.jar
Conclusion
In this article,we demonstrated how to delete unused memory space in Lotus Quickr, using the Quickr Web services API. The attached code sample shows how to do the cleanup for all the libraries in the Quickr repository, and you can start by trying it out with just one library, using its corresponding library id. If you accidentally delete an empty folder, just run the restore utility script (source code attachment) by passing the folder name and path taken from the log file.
Resources
Lotus Quickr documentation:
https://www.ibm.com/developerworks/lotus/documentation/quickr/
Lotus Quickr forum:
http://www-10.lotus.com/ldd/quickplace.nsf?OpenDatabase
developerWorks Lotus Quickr product page:
http://www.ibm.com/developerworks/lotus/products/quickr/
About the author
Annapoorani Rajagopal is a Senior System Engineer for IBM INDIA PVT LTD., currently working as a Java Application Developer. Her experience includes work in J2EE Applications in the CORE Banking domain, WebSphere Portal applications, Atlassian Products like Confluence and in IBM tools such as Lotus Quickr. You can reach her at
annrajag@in.ibm.com.