Posts Tagged: ‘xpages’
XPages: Interactive Maps using OpentStreetMap and Leaflet.js
42. DNUG Konferenz: Preview auf den Techniktrack Entwicklung
Track 2.4: Technik - Entwicklung
11. Juni 2015, 8:30 bis 15:45 Uhr
Integration von Salesforce und IBM Software - Andreas Rosen, QKom GmbH
Domino im Dialog mit einer OpenSource-Workflow-Engine - Jens Ribbeck / Veit Weber, ULC Business Solutions GmbH
Speed up your development in XPages or Javascript using Open Source - Tim Clark, Teamstudio (angefragt)
Vom XPages Held zum OSGI Guru! Wie man eine Extension Library entwickelt. Christian Güdemann, WebGate Consulting AG
XPages auf Bluemix - Niklas Heidloff, IBM
Track Manager sind Oliver Busse, We4IT GmbH, und Thorsten Hindermann, GWDG.
Hier erfahren Sie mehr über die 42. DNUG Konferenz in Dortmund:
Übersicht über Sponsoren und Aussteller mit Kontaktmöglichkeit
XPages: Running Google’s Chrome V8 Javascript Engine (2)
A while ago I tried to run Google’s V8 Javascript engine on top of XPages, and today I found the reason why my server crashed after the first click: I have tried to load the engine only once (statically), and that killed Domino completly.
Today I moved the code back into the processAction method, and now it works without problems.
package ch.hasselba.xpages.jav8;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class JAV8Test implements javax.faces.event.ActionListener {
public void processAction(ActionEvent actionEvent)
throws AbortProcessingException {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("jav8");
try {
System.out.println(engine.getClass().getCanonicalName());
engine.eval("var i=1+1;");
System.out.println("i = " + engine.get("i"));
} catch (ScriptException ex) {
ex.printStackTrace();
}
}
}
XPages: Empty HTML5 Attibutes & PassThroughTags
A while ago I developed some HTML5 XPages applications, but the development process was a little bit frustrating because of the missing possibility to add empty attributes to a PassThroughTag. A single empty attribute is not allowed, because this would result in invalid XML, and you cannot use “xp:attributes” with “UIPassThroughTag” components.
A simple example like this…
<input type="text" value="John Doe" disabled />
… always ended up in something like this:
<xp:text tagName="input" disableTheme="true">
<xp:this.attrs>
<xp:attr name="disabled" minimized="true" value="disabled" />
<xp:attr name="value" value="John Doe" />
</xp:this.attrs>
</xp:text>
To fit my requirements, I had extended the “UIPassThroughTag” with a special attribute named “emptyAttrs“. This allowed me to write the example above in the following syntax:
<input type="text" value="John Doe" emptyAttrs="disabled" />
(Multiple empty attributes can be added comma separated.)
Here is the Java class:
package ch.hasselba.xpages;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.ibm.xsp.component.UIPassThroughTag;
import com.ibm.xsp.renderkit.html_basic.PassThroughTagRenderer;
import com.ibm.xsp.webapp.XspHttpServletResponse;
public class PassThroughTagRendererEx extends PassThroughTagRenderer {
private final static String EMPTY_ATTRIBUTE_NAME = "emptyAttrs";
public void encodeBegin(FacesContext fc, UIComponent uiComponent)
throws IOException {
// Component is rendered?
if (uiComponent.isRendered() == false) {
return;
}
// only process instances of UIPassThroughTags
if ((uiComponent instanceof UIPassThroughTag)) {
UIPassThroughTag uiPTTag = (UIPassThroughTag) uiComponent;
ResponseWriter rw = fc.getResponseWriter();
// grab the printer writer directly from the response
XspHttpServletResponse response = (XspHttpServletResponse)
fc.getExternalContext().getResponse();
PrintWriter rwPrinter = response.getWriter();
// start the element tag
rw.startElement(uiPTTag.getTag(), uiComponent);
// process all attributes
List<UIPassThroughTag.TagAttribute> attrList = uiPTTag
.getTagAttributes();
if (attrList != null) {
UIPassThroughTag.TagAttribute tagAttribute = null;
String attrName = null;
String attrValue = null;
for (int i = 0; i < attrList.size(); i++) {
tagAttribute = attrList.get(i);
attrName = tagAttribute.getName();
attrValue = tagAttribute.getValue();
if (EMPTY_ATTRIBUTE_NAME.equalsIgnoreCase(attrName)) {
// process all empty tags
String tags[] = attrValue.split(",");
for( int j=0; j<tags.length; j++){
// write the attribute name only
rwPrinter.write( " " );
rwPrinter.write( tags[j].trim() );
}
}else{
// write the attribute data
rw.writeAttribute(attrName, attrValue, null);
}
}
}
} else {
// process other instances "as always"
super.encodeBegin(fc, uiComponent);
}
}
}
To activate the class you have to overwrite the renderer in the “faces-config.xml“:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<render-kit>
<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>com.ibm.xsp.PassThroughTagRenderer</renderer-type>
<renderer-class>ch.hasselba.xpages.PassThroughTagRendererEx</renderer-class>
</renderer>
</render-kit>
</faces-config>
Notes-Funktionen in JavaScript
EntwicklerCamp 2015: XPages – WebServices und REST
Der letzte Tag des EntwicklerCamps ist gekommen. Heute war mein zweiter Vortrag zum Thema "XPages - WebServices und REST". In 90 Minuten habe ich ausführlich beschrieben, wie Webservices und RESTful Webservices innerhalb von XPages konsumiert werden können. Die erste Hälfte des Vortrages konzentrierte sich auf das Backend mittels Java. In der zweiten Hälfte habe ich den Blick auf JavaScript im Browser gerichtet.
Wie immer können die Folien und die Beispiel-Anwendung heruntergeladen werden. Basis für diesen Vortrag war mein Vortrag auf der IBM ConnectED 2015: BP 108 - Be Open - Use Web Services and REST in XPages Applications. Insofern ist die Beispiel-Anwendung die gleiche wie beim IBM ConnectED-Vortrag.
In der Beispiel-Anwendung befinden sich nicht nur XPages, um die Webservices und RESTful Webservices zu konsumieren. Damit die Beispiele auch bei Ihnen ohne Probleme laufen, sind ein Webservice und drei RESTful Webservices implementiert.
EntwicklerCamp 2015: Java Managed Beans
Der zweite Tag des EntwicklerCamps nähert sich dem Ende. Heute war mein Vortrag "Java Managed Beans". Nach einer kurzen Einführung in Java Managed Beans haben wir uns die Verwendung in XPages angesehen. Wobei gerade auch das Verständnis für die internen Abläufe in der XPages-Laufzeitumgebung ein wichtiger Aspekt war. Am Schluß wurden die Einsatzmöglichkeiten diskutiert.
Wie immer können die Folien und die Beispiel-Anwendung heruntergeladen werden.
IBM Bluemix für “zu Hause”: Bluemix – Local
Raspberry Pi vs. IBM Bluemix – 1:0
I had some time last night (the whole family had gone to bed early), so I spent some to look at the XPages integration into Bluemix. I found the Greenwell Travel Expenses Demo:
But after clicking a link, the page returned an error:
Hmm…But I wanted to see the application!
That’s why I checked, if the datasources are protected. I recommend this for years. Fredrik Norling wrote a little snippet for this. Or better use the “ignoreRequestParam“. Then all your problems are gone.
http://greenwellexpenses.mybluemix.net/bluemix/Expense-App-Design.nsf/teamExpenses.xsp?databaseName=homepage.nsf
Bingo! Unprotected!
I now was able see a little bit more of the application and to check the underlying environment. But then came the moment where my brain forced me to try out some things:
First, I had to look again on the IP address in the error page: “109.228.14.66“. This is not an internal address. Let’s check it:
Not reachable. Whois for “109.228.14.66” ? “Fasthosts Internet Limited“. A provider in UK.
A ping to “greenwellexpenses.mybluemix.net” returned “75.126.81.6″, which belongs to Softlayer. The server is allowed access other servers? Maybe the application can call me?
Yes, the application can:
Now I can try to DoS the application. Because the outgoing connection from the application waits for a response (think about “telnet www.example.com 80“), I can create a bunch of requests, and the server will quickly reach it’s limit.
That’s why I created a simple bash script which makes HTTP request to the Bluemix instance. The script runs on a Raspberry Pi, to demonstrate the low demand of hardware requirements and to show how easy it is do make a DoS attack against a XPage application (if it is was not developed under security aspects).
Here is a short video (the source of the bash script is NOT shown, but it has fewer then 10 lines of code):
This was a “friendly” attack. I have not done anything harmfull. And this is a demo app; if it is not secure, this is not a real problem.
But last night I searched for some XPages servers in the WWW, and I found a lot of misconfigured systems: Error Page enabled, the “Ignore request parameter” is not set to “true” or at least the hack from Fredrik running. And the servers are allowed to access the whole internet… Dev’s and Admins should do their jobs better!
If you plan to migrate your apps to the cloud, please learn more about security. Or hire some specialists with the knowledge and experience in this sector. It is worth the time and the money.
Autosave when deleting attachments on an XPage
To download attachments in an application and also to be able to delete them, the FileDownload control is used on IBM Domino XPages. Unfortunately, this control comes with a little twist. If you click on the trash can icon to delete the attachment, it looks like the file is actually deleted. But the file is not finally deleted until the XPage is saved.
If you leave the XPage after the click on the trash can icon, without saving the document – after all, the attachment disappeared from the view, so why SHOULD you save the document anyway – and open it some time later again, the file is back.
Img. 1: Document including several attachments
Img. 2: Document after clicking the trash can icon of the last attachment
Img. 3: The last attachment is back again after leaving the document without saving and coming back to it afterwards
In order to execute the change, or rather the deletion, directly within the document, it is necessary to add the property setSave(true) to the event handler of the trash can icon. Unfortunately, you cannot do this within the Domino Designer. So a little programming work is needed.
Luckily I have found the appropriate code for most of this work in a discussion comment on stackoverflow by Sven Hasselbach: http://stackoverflow.com/questions/13101615/auto-save-doc-after-delete-of-attachment-in-file-download-control
Unfortunately, the original code always produced the following error when I used it with Notes 9:
X XPAGE ERROR: COM.XSP.IBM.ACTION.ACTIONGROUP INCOMPATIBLE WITH COM.IBM.XSP.ACTIONS.DELETEATTACHMENTSACTION
Therefore, I adjusted the source code, so that it works with Notes 8.5.3 and Notes 9 alike. To use it properly you only need to insert it into the BeforeRenderResponse event of the XPage.
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
function overrideFileDownloadAction( fDownload ){
//Check whether the control exists or not
if( fDownload === null )
return;
//Call actual function
rekOverrideFileDownloadAction( fDownload, fDownload );
}
/*
*This control iterates through all children of the FileDownload control
*and seeks the event handler
*/
function rekOverrideFileDownloadAction( component:javax.faces.component.UIOutput,fDownload:com.ibm.xsp.component.UIFileDownload ){
try{
//Get children of the current element
var children:java.util.List = component.getChildren();
var it:java.util.Iterator = children.iterator();
var curChild:javax.faces.component.UIOutput;
//Loop over all children
while( it.hasNext() ){
curChild = it.next();
if( typeof( curChild ) === 'com.ibm.xsp.component.xp.XspEventHandler' ){
//Event handler found
//set setSave to true
// to automatically save after the event
curChild.setSave(true);
}
//Event handler not found, therefore recursive call to investigate the children
rekOverrideFileDownloadAction( curChild , fDownload );
}
}catch(e){}
}
overrideFileDownloadAction( getComponent( 'IDFILEDOWNLOAD' ) );
//IDFILEDOWNLOAD = the ID of the FileDownload control
}]]>
</xp:this.beforeRenderResponse>
If the above code is inserted into your XPage, the file is automatically saved after clicking on the trash can icon. The disadvantage of this is that it saves the complete document and not just delete the attachment. Therefore, you have to be careful to ensure that you have not previously changed the value of another field that should not be saved.
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:
Re: Re: 41. DNUG Konferenz: Track Entwicklung am 12.11.2014
Als Antwort auf: Re: 41. DNUG Konferenz: Track Entwicklung am 12.11.2014
Gemeinsam mit meinem Team freue ich mich über dieses gute Feedback. Die positive Resonanz zu den Themen der beiden Techniktracks hatte sich schon bei der Vorauswahl der Sessions abgezeichnet.Ein herzliches Dankeschön auch von unserer Seite an alle Referenten und Track Manager.