XPages: WebContent Files (2) – Manipulate exitsting files using the Java NAPI

In this article, I will shortly give an overview how you can edit existing file from the WebContent folder (Don’t miss the first article on this topic).

First, let’s create a view to display the design elements of the WebContent folder. To do this, I have an old school LotusScript Agent which updates the selection formula of a view (Some details about this technique can be found here).

Sub Initialize

    Dim session As New NotesSession
    Dim doc As NotesDocument
    Dim db As NotesDatabase
    Dim view As NotesView
      
    Set db = session.Currentdatabase
    Set view = db.Getview("DesignView")
    
    view.SelectionFormula = |@Contains($FlagsExt; "w")|
    
    Set doc = db.GetDocumentByUNID(view.UniversalID)
    Delete view
    
    doc.ReplaceItemValue "$FormulaClass", "7FFF"
    doc.Sign
    doc.Save True, False

End Sub

The agent has to run once to change the view’s selection criteria. In this example the view has the name „DesignView“. After that, we can add a single column to the view to display the files and their names:

Now lets build a simple XPage named Files.xsp to select the file you want to edit:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    <xp:viewPanel
        rows="30"
        id="viewPanel1"
        var="rowEntry">
        <xp:this.facets>
            <xp:pager
                partialRefresh="true"
                layout="Previous Group Next"
                xp:key="headerPager"
                id="pager1">
            </xp:pager>
        </xp:this.facets>
        <xp:this.data>
            <xp:dominoView
                var="viewDesign"
                viewName="DesignView">
            </xp:dominoView>
        </xp:this.data>
        <xp:viewColumn
            columnName="$FileNames"
            id="viewColumnFileNames"
            displayAs="link"
            >
            <xp:this.pageUrl>
                <![CDATA[#{javascript:"/Editor.xsp?filename=" +
                   rowEntry.getColumnValues().get(0)}]]>
        </xp:this.pageUrl>
        </xp:viewColumn>
    </xp:viewPanel>
    <xp:br />

</xp:view>

The Files.xsp lists all files as links and opens them via the Editor.xsp XPage:

This is the Editor.xsp:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">

    FileName:
    <xp:inputText
        id="inputTextFileName"
        value="#{fileBean.fileName}"
        defaultValue="#{param.filename}"
        disabled="true" />
    <xp:br />
    FileData:

    <xp:inputTextarea
        id="inputTextarea1"
        value="#{fileBean.fileData}"
        rows="40"
        cols="80" />
    <xp:br />
    <xp:button
        value="Load"
        id="buttonLoad">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            action="#{fileBean.loadData}">
        </xp:eventHandler>
    </xp:button>
    <xp:button
        value="Save"
        id="buttonSave">
        <xp:eventHandler
            event="onclick"
            submit="true"
            refreshMode="complete"
            action="#{fileBean.saveData}">
        </xp:eventHandler>
    </xp:button>
    
</xp:view>

It uses a simple managed bean…

package ch.hasselba.napi;

import java.io.Serializable;

public class FileDataBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private String fileName;
    private String fileData;
    private String dbPath;
    private String dbServer;

    public String getDbPath() {
        return dbPath;
    }

    public void setDbPath(String dbPath) {
        this.dbPath = dbPath;
    }

    public String getDbServer() {
        return dbServer;
    }

    public void setDbServer(String dbServer) {
        this.dbServer = dbServer;
    }

    public void setFileData(String fileData) {
        this.fileData = fileData;
    }

    public String getFileData() {
        return fileData;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }

    public void loadData() {
        this.fileData = NAPIUtils.loadFile(this.dbServer, this.dbPath, this.fileName);
    }

    public void saveData() {
        NAPIUtils.saveFile(this.dbServer, this.dbPath, this.fileName, this.fileData);
    }
}

… which is initialized with the properties defined in the faces-config.xml. There you can find the database server and the database path:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
  <managed-bean>
    <managed-bean-name>fileBean</managed-bean-name>
    <managed-bean-class>ch.hasselba.napi.FileDataBean</managed-bean-class>
    <managed-bean-scope>view</managed-bean-scope>
    <managed-property>
      <property-name>dbPath</property-name>
      <value>NAPI.nsf</value>
    </managed-property>
    <managed-property>
      <property-name>dbServer</property-name>
      <value>DEV01/Hasselba/CH</value>
    </managed-property>
  </managed-bean>
</faces-config>

And last but not least, the required NAPIUtil class:

package ch.hasselba.napi;

import java.io.InputStream;
import com.ibm.designer.domino.napi.NotesAPIException;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesObject;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;

public class NAPIUtils {

    /**
     * loads a given WebContent file and returns the result as String
     * 
     * @param serverName
     *            the server to use
     * @param dbPath
     *            the database path
     * @param fileName
     *            the file to load
     * @return the file data as String
     */
    static public String loadFile(final String serverName, final String dbPath,
            final String fileName) {

        NotesSession nSession = null;
        NotesDatabase nDatabase = null;
        NotesNote nNote = null;

        try {
            nSession = new NotesSession();

            // open database
            nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
            nDatabase.open();

            // load existing data
            nNote = FileAccess.getFileByPath(nDatabase, fileName);

            // get Filedate and return String
            InputStream is = FileAccess.readFileContentAsInputStream(nNote);

            return convertStreamToString(is);
        } catch (NotesAPIException e) {
            e.printStackTrace();
        } finally {
            // recycle NAPI objects
            recycleNAPIObject(nNote, nDatabase, nSession);
        }

        return fileName;
    }

    /**
     * loads a given WebContent file and returns the result as String
     * 
     * @param serverName
     *            the server to use
     * @param dbPath
     *            the database path
     * @param fileName
     *            the file to load
     * @param fileData
     *            the data of the file
     */
    static public void saveFile(final String serverName, final String dbPath,
            final String fileName, final String fileData) {

        NotesSession nSession = null;
        NotesDatabase nDatabase = null;
        NotesNote nNote = null;

        try {
            nSession = new NotesSession();

            // open database
            nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
            nDatabase.open();

            // load existing data
            nNote = FileAccess.getFileByPath(nDatabase, fileName);

            // store them to note
            FileAccess.saveData(nNote, fileName, fileData.getBytes());

        } catch (NotesAPIException e) {
            e.printStackTrace();
        } finally {
            // recycle NAPI objects
            recycleNAPIObject(nNote, nDatabase, nSession);
        }
    }

    /**
     * converts an input stream to a string
     * 
     * @param is
     *            the input stream to convert
     * @return String
     */
    static String convertStreamToString(java.io.InputStream is) {
        java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

    /**
     * recycleNAPIObject helper method for recycling NAPI objects
     * 
     * @param nObjects
     *            the NAPI objects to recycle
     */
    static void recycleNAPIObject(NotesObject... nObjects) {
        for (NotesObject nObject : nObjects) {
            if (nObject != null) {
                try {
                    (nObject).recycle();
                } catch (NotesAPIException ne) {
                }
            }
        }
    }
}

If the class looks like this…

…just hover one  of the entries and select „Fix project setup“.

Then, you can choose the missed bundle:

Build the project, and open one of the files by clicking a link in the Files.xsp. Here is an example for the file WEB-INF/faces-config.xml:

Now you can click the „Load“ button to read the content of the file.

You can edit the file now and save it to the NSF.

If you got to package explorer (Hit F9 for refresh) you can see the changes:

Dieser Beitrag wurde unter Java, Server, XPages abgelegt und mit , , , , , verschlagwortet. Setze ein Lesezeichen auf den Permalink.

Ein Kommentar zu XPages: WebContent Files (2) – Manipulate exitsting files using the Java NAPI

  1. Hi Sven, I thought your approach would save my problem. I an app I want to create presentation files based on a template. I thought a good place to store the template would be somewhere in the webcontent folder of the NSF. So I added the lwpd.domino.napi.jar to the build path but when I run my code I get the log message:

    2016-09-27 21:09:46 HTTP JVM: com.ibm.designer.domino.napi.NotesAPIException: Error while opening a collection
    2016-09-27 21:09:46 HTTP JVM: This function is inappropriate for file system directories.
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.NotesDatabase.NOpenCollection(Native Method)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.NotesDatabase.openCollection(NotesDatabase.java:650)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.design.FileAccess.getFileByPath(FileAccess.java:69)
    2016-09-27 21:09:46 HTTP JVM: at ch.hasselba.napi.NAPIUtils.loadFile(NAPIUtils.java:39)
    2016-09-27 21:09:46 HTTP JVM: at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    2016-09-27 21:09:46 HTTP JVM: at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
    2016-09-27 21:09:46 HTTP JVM: at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
    2016-09-27 21:09:46 HTTP JVM: at java.lang.reflect.Method.invoke(Method.java:611)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.JavaAccessObject.call(JavaAccessObject.java:321)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.FBSObject.call(FBSObject.java:161)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTCall.interpret(ASTCall.java:197)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTVariableDecl.interpret(ASTVariableDecl.java:82)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject._executeFunction(FunctionObject.java:261)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject.executeFunction(FunctionObject.java:185)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject.call(FunctionObject.java:171)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.FBSObject.call(FBSObject.java:161)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTCall.interpret(ASTCall.java:197)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTProgram.interpret(ASTProgram.java:119)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTProgram.interpretEx(ASTProgram.java:139)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression._interpretExpression(JSExpression.java:435)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.access$1(JSExpression.java:424)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression$2.run(JSExpression.java:414)
    2016-09-27 21:09:46 HTTP JVM: at java.security.AccessController.doPrivileged(AccessController.java:362)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.interpretExpression(JSExpression.java:410)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.evaluateValue(JSExpression.java:251)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.evaluateValue(JSExpression.java:234)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.javascript.JavaScriptInterpreter.interpret(JavaScriptInterpreter.java:222)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.binding.javascript.JavaScriptMethodBinding.invoke(JavaScriptMethodBinding.java:111)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.component.UIViewRootEx.invokePhaseMethodBinding(UIViewRootEx.java:1735)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.invokePhaseMethodBinding(FacesControllerImpl.java:450)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.access$0(FacesControllerImpl.java:444)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl$ViewPhaseListener.beforePhase(FacesControllerImpl.java:533)
    2016-09-27 21:09:46 HTTP JVM: at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:197)
    2016-09-27 21:09:46 HTTP JVM: at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:120)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.render(FacesControllerImpl.java:270)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServlet.serviceView(FacesServlet.java:261)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServletEx.serviceView(FacesServletEx.java:157)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServlet.service(FacesServlet.java:160)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServletEx.service(FacesServletEx.java:138)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.DesignerFacesServlet.service(DesignerFacesServlet.java:103)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:576)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1335)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:853)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:796)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:565)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1319)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:662)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:482)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:357)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:313)
    2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)

    when I then check the name of the file it gives me the correct name so I think it still looks good.

    var template:String = toolbox.loadFile(„“,database.getFilePath(),“template.pptx“);
    print(template);

    if I then try a open a file inputstream it breaks:

    var inputstream:FileInputStream = new FileInputStream(template);

    btw I am using SSJS (forced to do so).

    do you have any idea what I have overlooked?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.


*