If you're frustrated with Domino's limitations on scheduling agents to run more frequently than once every 5 minutes, you're not alone. As a programmer, you understand the need for flexibility and control in your applications. In this article, we'll discuss a practical solution: creating a JavaAddin for Domino that can trigger agents at shorter intervals, allowing you to gain more fine-grained control over your scheduled tasks.
Understanding the Domino Agent Scheduler
Domino provides a robust environment for running scheduled agents. However, it imposes a minimum time gap of 5 minutes between consecutive runs of the same agent. This limitation can be a roadblock for applications that require more frequent execution.
The Power of JavaAddins
JavaAddins offer a way to extend Domino's functionality using Java code. This opens up a world of possibilities, including overcoming the 5-minute scheduling restriction. Here's how you can do it:
1. Setting Up Your JavaAddin
To get started, you'll need to create a JavaAddin. This involves writing Java code to interface with Domino. The code should enable you to trigger agents at shorter intervals than what Domino's native scheduling allows.
2. Utilizing Timers
One of the most effective ways to bypass the 5-minute limitation is to use timers in your JavaAddin. With timers, you can execute your agent at precise intervals, even down to seconds. Here's a simplified example of how this could look in your Java code:
import lotus.domino.*;
public class CustomScheduler extends JavaServerAddin {
public void runNotes() {
try {
Session session = NotesFactory.createSession();
Database database = session.getDatabase("", "YourDatabase.nsf");
Agent agent = database.getAgent("YourAgent");
// Set the execution interval in milliseconds
int interval = 30000; // 30 seconds
while (true) {
agent.runWithDocumentContext(null);
sleep(interval);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
This code defines a Java thread that runs your specified agent every 30 seconds, effectively bypassing Domino's 5-minute restriction.
3. Deploying Your JavaAddin
Once you've created your JavaAddin, you need to deploy it within your Domino environment. Ensure that the necessary permissions and access controls are in place.
4. Monitoring and Maintenance
Regularly monitor the execution of your custom scheduling solution. Ensure that it's working as expected and doesn't place undue stress on your Domino server.
Conclusion
By creating a JavaAddin for Domino that can trigger agents more frequently, you can take control of your scheduling needs. This solution empowers you to run your agents at shorter intervals, achieving the level of precision your applications require. While this approach requires some development effort, the benefits of fine-grained agent scheduling can greatly enhance your Domino-based applications.
In summary, if you're tired of being constrained by Domino's 5-minute scheduling limitation, consider the power of JavaAddins to break free and gain control over your scheduled agents.
I have also created a bit more advanced setup which you can get on github: DominoAgentsHelper
Stay tuned for more technical insights, delivered directly to the point, in future articles.
There are plenty of different Java template engines but for last years I used to stick to FreeMarker. It's open sourced and licensed under the Apache License, Version 2.0.
Here I only want to demonstrate how to integrate it with Domino nicely as it requires to write TemplateLoader class.
Build result based on tempalte "page"
Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
Get the template (uses cache internally)
DominoTemplateLoader dominoLoader = new DominoTemplateLoader(getDatabase());
cfg.setTemplateLoader(dominoLoader);
Template template = cfg.getTemplate("page");
/* Merge data-model with template */
HashMap tags = new HashMap();
tags.put("title", "hellow world");
tags.put("description", "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.");
Writer out = new StringWriter();
template.process(tags, out);
String html = out.toString();
The most important part was actually to build DominoTemplateLoader class and below you can see it
One of my customer moves to Office 365 and also wants to move to to Exchange/Outlook 356 during next year while keeping Domino app running as it is of now.
They have customized mail boxes with few actions which allow to journalize emails into their Domino applications and that is quite critical functionality so that would need to be mirrored.
We have decided to built Outlook Add-in (works in web, client and also with mobile devices).
Here are a few advises to those who would need to developer similar functionality.
1. Create a outlook add-in project and define manifest
You would have to create a project with manifest and needed html, css, js elementets. You can easily find information how to do that on MS sites (not going to provide any links as they could change in future). That will allow you to define UI, see my example.
2. Send memo ID to Domino
We need to get information about email from Outlook Add in and send those items to Domino (you would have to build REST API on Domino side that can receive data from Outlook).
Office.context.mailbox.getCallbackTokenAsync(function(result) {
...
var token = result.value;
var ewsurl = Office.context.mailbox.restUrl;
var ewsItemId = Office.context.mailbox.item.itemId;
const itemId = Office.context.mailbox.convertToRestId(ewsItemId,Office.MailboxEnums.RestVersion.v2_0);
// send token, ewsurl and itemId to Domino endpoint
...
}
Having those keys (token url and itemId) you can pull email in Mime format
3. Convert Mime to Notes email
So at this point Domino received data from Add in and can finally do another request to Exchange server (using token, ewsurl and itemId) to read the memo MIME
Dim http As NotesHTTPRequest
Dim enpoint As string
Set http = m_app.NotesSession.Createhttprequest()
Call http.Setheaderfield("Authorization", "Bearer " + token)
enpoint = ewsurl + |/v2.0/me/messages/| + itemId + |/$value|
getItemMIME = http.get(enpoint)
There is no native Domino LS/Java Mime Parser however I found working example by Stephan: Importing EML files into Notes (lots of them). It worked well, but seems it does not handle inline images (need to do more testing etc).
Alternatively I was told that there is MimeMessageParser.java that writes MIME to a Notes document. This class is part of the XPages Extension Library. So it has sense to compare them.
I have a form with some text fields and I also needed to send attachments within same form.
Form is printed by agent and is processed by another agent written in LotusScript.
I spent some time working on solution and here it is.
The idea is to convert selected files to base64 on client side and then post data on submission and agent that process submission will conver base64 to file.
Here is a form, note that we run some logic when files are added
Here is how we convert selected files to base64 and how we results as text fields to form (JS is not optimal, it can be done without jQuery)
function toBase64() {
var files = document.querySelector('input[type=file]').files;
var form = $("form");
form.find("input[name^='filebase64']").remove(); // replace
function readAndSave(file, index) {
var reader = new FileReader();
reader.addEventListener("load", function() {
form.append("");
form.append("");
}, false);
reader.readAsDataURL(file);
}
if (files) {
[].forEach.call(files, readAndSave);
}
}
Once form is submitted we have to read base64 items and convert them to file. There are at least 2 solutions: pure LS or Java/LS2J
a) LotusScript using NotesMIMEHeader
Private Function saveBase64AsFile(base64 As String, filePath As string) As Boolean
On Error GoTo ErrorHandler
Dim stub As NotesDocument
Dim stream As NotesStream
Dim item As NotesMIMEEntity
Dim header As NotesMIMEHeader
Dim emb As NotesEmbeddedObject
Dim fileName As String
Dim contentType As string
Dim base64File As String
fileName = StrRightBack(filePath, "\")
contentType = StrRight(Strleft(base64, ";"), ":")
base64File = StrRight(Base64, ",")
Call scriptLog.loginfo(fileName)
Call scriptLog.loginfo(contentType)
Set stub = db.Createdocument()
Set item = stub.CreateMIMEEntity("Body")
Set header = item.createHeader("Content-Disposition")
Call header.setHeaderVal({attachment; filename="} & fileName & {"})
Set stream = app.NotesSession.CreateStream()
Call stream.WriteText(base64File)
Call item.SetContentFromText(stream, contentType, ENC_BASE64)
Call stream.Truncate
Call stream.Close
Call stub.Closemimeentities(True)
Set emb = stub.Getattachment(fileName)
Call emb.Extractfile(filePath)
Exit Function
ErrorHandler:
Error Err, Error
End Function
UseLSX "*javacon"
Use "Base64ToFile"
Class Base64ToFile
Private jSession As JavaSession
Private jClass As Javaclass
Private jObject As JavaObject
Private jError As JavaError
Sub New()
Set jSession = New JavaSession
Set jClass = jSession.GetClass("Base64ToFile")
Set jObject = jClass.Createobject()
End Sub
Public Function convert(base64 As String, filePath As String) As Boolean
convert = jObject.convert(base64, filePath)
End Function
End Class
In this article we will improve our java addin 'DemoAddin' with following features:
Constructor that accepts parameters when we load java addin from console.
We will schedule output to console amount of registered person in names.nsf (every 33 seconds).
Define destructor.
Load (with parameter) and Unload addin from console
Constructor
We will define 2 constructor, one that can accept parameters and one in case if we load addin without any parameters. It is pretty obvious how it works.
// we expect our first parameter is dedicated for secondsElapsed
public DemoAddin(String[] args) {
this.secondsElapsed = Integer.parseInt(args[0]);
}
// constructor if no parameters
public DemoAddin() {}
Means if we run command like below it will run using constructor with parameters
load runjava org.demo.DemoAddin 33
Schedule worker
There is a method (it works but deprecated, however I have not found what should be use instead). The snippet below run constant loop, open names.nsf, read amount of users in the view People and output it to console. The main line here is this.addInRunning(), it keeps loop running forever (until we change it to this.stopAddin() or unload addin from console)
session = NotesFactory.createSession();
String server = session.getServerName();
while (this.addInRunning()) {
/* gives control to other task in non preemptive os*/
OSPreemptOccasionally();
if (this.AddInHasSecondsElapsed(secondsElapsed)) {
ab = session.getDatabase(server, "names.nsf");
long count = ab.getView("People").getAllEntries().getCount();
logMessage("Count of persons: " + Long.toString(count));
ab.recycle();
}
}
Destructor
Keep in mind that we need to be careful with Notes object, we have to release memory (recycle) after we no longer use them. So it's a good idea to create own terminate method that release memory for all Notes object you delcared and use it when addin unloads. There is also built-in method finalize so you can put code there, but I prefer to have own method and use it in places I need
Load (with parameter) and Unload addin from console
In order to run addin with parameter simply add it after name of Addin, use space as a separator when you need more than 1 parameter
load runjava org.demo.DemoAddin 33
[1098:0002-23A0] 10/20/2020 11:15:00 AM JVM: Java Virtual Machine initialized.
[1098:0002-23A0] 10/20/2020 11:15:00 AM RunJava: Started org/demo/DemoAddin Java task.
[1098:0004-3984] 10/20/2020 11:15:00 AM DemoAddin: version 2
[1098:0004-3984] 10/20/2020 11:15:00 AM DemoAddin: build date 2020-10-19 11:00 CET
[1098:0004-3984] 10/20/2020 11:15:00 AM DemoAddin: java 1.8
[1098:0004-3984] 10/20/2020 11:15:00 AM DemoAddin: seconds elapsed 33
[1098:0004-3984] 10/20/2020 11:15:33 AM DemoAddin: Count of persons: 11
[1098:0004-3984] 10/20/2020 11:16:06 AM DemoAddin: Count of persons: 11
[1098:0004-3984] 10/20/2020 11:16:39 AM DemoAddin: Count of persons: 11
[1098:0004-3984] 10/20/2020 11:17:12 AM DemoAddin: Count of persons: 11
When you want to unload addin using console here is a command
tell runjava unload org.demo.DemoAddin
And you should be see confirmation on console if everything went fine
[1098:0004-3984] 10/20/2020 11:26:55 AM DemoAddin: UNLOADED (OK)
[1098:0002-23A0] 10/20/2020 11:26:55 AM RunJava: Finalized org/demo/DemoAddin Java task.
[1098:0002-23A0] 10/20/2020 11:26:56 AM RunJava shutdown.
If you are interested in this topic I can recommend at least two more sources
NSFTools.com JavaAddinTest and AndyBrunner / Domino-JAddin or wait for new articles in my blog :-). Also feel free to ask questions if you are uncertain.
Full version of DemoAddin class is hosted on github: DominoDemoAddin
I will show how to build, register and load simple JavaAddin for Domino. I'm not entirely sure if lotus.notes.addins.JavaServerAddin is supported by HCL, so use that for your own sake.
1) Java class
import lotus.notes.addins.JavaServerAddin;
public class DemoAddin extends JavaServerAddin {
public void runNotes() {
AddInLogMessageText("Hello world", 0);
}
}
2) JAR - from project
Export/build JAR file from the DemoAddin project (we are going to put jar file in the Domino folder).
3) Register JavaAddin
Place JAR file under Domino, f.x. path could be (DemoAddin is a folder and it could be just any name, DemoAddin-1.jar is our JAR file we built earlier)
C:\IBM\Domino\DemoAddin\DemoAddin-1.jar
and then register it in server's notes.ini using variable JAVAUSERCLASSES. In case if there are other addin registered there use semicolon as a separator for Windows and a colon for Linux
Alternatively simply put JAR file into the folder \jvm\lib\ext, but personally I prefer to keep customization separately instead of mixing core JAR files with customization. Additionally I'm not sure what happens to custom JAR file when is upgradet.
4) Load JavaAddin
It's time to run our DemoAddin. From console run following command
load runjava DemoAddin
Take into account if your include your class into a package, f.x. package org.demo; than you should add that into run command
load runjava org.demo.DemoAddin
If everything went fine you should see 3 lines
RunJava: Started DemoAddin Java task.
Hello world
RunJava: Finalized DemoAddin Java task.
Possible error
If you registered JAR file incorrectly, the error could be like this. In such case just make sure you followed steps properly.
RunJava: Can't find class DemoAddin1 or lotus/notes/addins/demoaddin1/DemoAddin1 in the classpath. Class names are case-sensitive.
If I find time, I will make few more posts about this topic. It's really just a tip of the iceberg.
Recently I had to made proper formatting of NotesDateTime object (using LotusScript) with respect to different locale. I had written a cover in LS2J for Java SimpleDateFormat class that works independently (without Java library), so copy/paste and go on.
Here is an example how it works
Dim jdtr As New jDateTimeRich
Dim dt As New NotesDateTime("15-10-2017 10:20:30")
MsgBox jdtr.SimpleDateFormat(dt, "dd-MM-yyyy", "", "") ' "15-10-2017"
MsgBox jdtr.SimpleDateFormat(dt, "d-MMM-yy", "ru", "RU") ' "15-окт-17"
MsgBox jdtr.SimpleDateFormat(dt, "EEEEE MMMMM yyyy HH:mm:ss.SSSZ", "da", "DK") ' "søndag oktober 2017 11:20:30.000+0200")
The beautiful part of it - is translation of months and days according to Locale.
I have uploaded source/class on github. Feel free to re-use it: jDatetimeRich-LS and report issues of course.
Recently I have faced an issue where one of our provider changed SSL and they disabled supporting of TLS 1.0 (as far as I understand it's non secure ourdays) and TLS 1.2 should be used instead. As a result our java agents (which used HttpsURLConnection) could not connect anymore to provider.
Enable TLS 1.2 on Domino (applicable only for 9.0.1 FP3 IF2 and higher)
The Domino JVM is based on Java 1.6 and default settings configured in a way to use TLS 1.0. Luckily our Domino servers had version 9.0.1 FP4 (and TSL 1.2 support has been added since FP3 IF2). So our version was capable to work with 1.2 (in theory) but it took some time to make it work.
In order to configure Domino JVM to use TLS 1.2 you need to:
After you added settings don't forget to restart Domino server. Keep in mind that setting is global meaning all agents that will start to use TLS1.2 therefore it is definitely worth to verify everything before and after this fix.
Java library solution
If that is not a way you can go with (f.x. Domino has lower version or something won'f work if you switch to TLS 1.2) then it's still possible to make custom Java Library that will make it possible, see link: How to use TLS 1.2 in Java 6.
It worked for me as well, but it requires to give permission in java policy on Domino server.
When you wanna compare String object with string literal, we often disregard what we compare with what, however there is one and safe way to do it.
It's much better to use equals() and equalsIgnoreCase() for a string literal, instead of Object, because it helps to avoid possible NullPointerException.
Here is an example:
String a = null;
System.out.print("123".equals(a)); // false
System.out.print(a.equals("123")); // java.lang.NullPointerException
Just few typical mistakes developers do when dealing with Strings. It's common stuff and everybody knows that, but for some reasons I still find such things (even written by myself :-)).
1. Checking empty string
In old days (version 1.5 and lower) we used String.equal(""), but 1.6 brought us String.IsEmpty which is better and faster.
// wrong/slow
if (name.equals("")) {
// correct/fast
if (name.isEmpty()) {
2. Concatenation
Method String.concat creates new String object, it's OK to use it when you do operation only once, otherwise use operator + += operators (see below why).
String s = "Hello ".concat("new").concat(" world!"); // "Hello new world!"
Using operators + and +=, they do not use String.concat method but StringBuilder and it's important to know what it means to us.
String s = "Hello " + "new" + " world!"; // "Hello new world!"
What actually happens when you use + operator is StringBuilder used:
String s = new StringBuilder().append("Hello ").append("new").append(" world!").toString(); // "Hello new world!"
So conclusion - it's ok to use concat if you do one time operation, for any other situation I would recommend to use + or +=.
3. String Formatting
Very often in order to format string people either concatenate the string to achieve result or do replace or invent something else. I do it as well sometimes (what a shame!). Instead we should use String.format method to do that.
int n = 10;
// wrong
String s = "I have " + Integer.toString(10) + " books";
// wrong
String s = "I have #num books".replace("#num", Integer.toString(10));
// correct
Strig s = String.format("I have %d books", n);
I would be glad to hear other typical mistakes we do with String object! So please share your experience.
Recently I've faced with few mistake that drain hours from me. I had to make integration with web-serivce. I imported WSDL file and got java classes generated, than I according to documentation I checked connect method with credentials...
Error connecting to 'endpoint' on port '443', SSL invalid certificate, may need to cross-certify
Tried connect method again - it worked! Gonna be easy task now (I thought). However nothing else except connect method worked. All other method of consumer either did nothing or returned null to me. I was confused, spend few hours and than contacted web-service provider, they were not familiar with Java and provided me example in PHP that worked as expected.
My issue was I had to set SESSION_MAINTAIN_PROPERTY to true for my proxy object.
SecretServiceLocator service = new SecretServiceLocator();
SecretPortType port = service.getSecretPort();
((javax.xml.rpc.Stub)port)._setProperty(javax.xml.rpc.Stub.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
SESSION_MAINTAIN_PROPERTY
Standard property: This boolean property is used by a service client to indicate whether or not it wants to participate in a session with a service endpoint. If this property is set to true, the service client indicates that it wants the session to be maintained. If set to false, the session is not maintained. The default value for this property is false.
Once I set session maintain to true - everything started to work.
Sometimes datetime output become tricky in Lotus Notes.
F.x. imagine you have code like this and you have german or any another locale on user's PC.
Set dt = New NotesDateTime("")
Call dt.SetNow
msgbox Format$(dt.LSLocalTime, "dddd, dd. MMMM yyyy")
Out would be Tuesday, 22. December 2015
But what if you want output in german i.e.? 22. Dezember 2015
In such case you may want to use LS2J approach to display it properly to user
Below you will find a solution that either allow to set locale or use default locale.
Option Public
Option Declare
UseLSX "*javacon"
Sub Initialize
On Error GoTo errhandler
Dim jSession As New JavaSession
Dim jCalendarClass As Javaclass, jLocaleClass As JavaClass, jSimpleDateFormatClass As JavaClass
Dim jCalendar As Javaobject, jLocale As Javaobject, jSDF As JavaObject, jDate As Javaobject
Dim jError As JavaError
Dim dt As New NotesDateTime("")
Call dt.SetNow
'in order to initiate date - we have to use Calendar object
Set jCalendarClass = jSession.GetClass("java.util.Calendar")
Set jCalendar = jCalendarClass.getInstance()
Call jCalendar.set(Year(dt.DateOnly), month(dt.DateOnly) - 1, day(dt.DateOnly))
'initialize date object
Set jDate = jCalendar.getTime()
'IMPORTANT
'create locale (here we specify langauge/country code)
Set jLocaleClass = jSession.GetClass("java.util.Locale")
Set jLocale = jLocaleClass.CreateObject("(Ljava/lang/String;Ljava/lang/String;)V", "de", "DE")
'also you can use local settings, if you need that - enable line below
'Set jLocale = jLocaleClass.getDefault()
'output format
Set jSimpleDateFormatClass = jSession.GetClass("java.text.SimpleDateFormat")
Set jSDF = jSimpleDateFormatClass.CreateObject("(Ljava/lang/String;Ljava/util/Locale;)V", "dd. MMMM yyyy", jLocale)
'result
MsgBox jSDF.format(jDate)
done:
Exit Sub
errhandler:
Set jError = jSession.GetLastJavaError()
MsgBox "JavaError was " & jError.errorMsg
jSession.ClearJavaError
Resume done
End Sub
//1. go to opt folder
cd /opt
//2. download java
wget --no-cookies --no-check-certificate --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com%2F; oraclelicense=accept-securebackup-cookie" "http://download.oracle.com/otn-pub/java/jdk/8u60-b27/jdk-8u60-linux-x64.tar.gz"
//3. extract java archive
tar xzf jdk-8u60-linux-x64.tar.gz
//4. installing java
cd /opt/jdk1.8.0_60/
alternatives --install /usr/bin/java java /opt/jdk1.8.0_60/bin/java 2
alternatives --config java
//5. recommended options: setup javac and jar
alternatives --install /usr/bin/jar jar /opt/jdk1.8.0_60/bin/jar 2
alternatives --install /usr/bin/javac javac /opt/jdk1.8.0_60/bin/javac 2
alternatives --set jar /opt/jdk1.8.0_60/bin/jar
alternatives --set javac /opt/jdk1.8.0_60/bin/javac
//6. setup variables so we can access java from everywhere
export JAVA_HOME=/opt/jdk1.8.0_60
export JRE_HOME=/opt/jdk1.8.0_60/jre
export PATH=$PATH:/opt/jdk1.8.0_60/bin:/opt/jdk1.8.0_60/jre/bin
//7. checking java version
java -version
//8. delete java archive
rm jdk-8u60-linux-x64.tar.gz
Installing Play Framework / TypeSafe
Example is about current version, which is 1.3.6
//1. go to tmp folder and download typesafe activator
cd /tmp
wget https://downloads.typesafe.com/typesafe-activator/1.3.6/typesafe-activator-1.3.6.zip
//2. unzip and move to opt folder
unzip typesafe-activator-1.3.6.zip
mv activator-dist-1.3.6 /opt
//3. delete zip file, we do not need it anymore
rm typesafe-activator-1.3.6.zip
//4. create soft symbolic link from /opt/activator-dist-1.3.6/activator to /usr/local/sbin/activator
ln -s /opt/activator-dist-1.3.6/activator /usr/local/sbin/activator
//5. set variables so we can use activator from everywhere
export PATH=/usr/local/sbin/activator:$PATH
Create helloworld project
Time to create our first project. Play Framework provides many templates, we are going to use play-java (it has already web interface and some code, so we can discover it).
//1. create folder www in /var (we are going to keep projects there).
cd /var
mkdir www
cd www
//2. have a look on current templates
activator list-templates
//3. create a new project based on play-java template
activator new helloworld play-java
//4. go into newly created project and run it
cd helloworld
activator run
If activator run properly, you should see something like this:
Now you are ready to open project in browser. Type 127.0.0.1:900 and boooom, we have it ready!
I'm going to play with centOS little bit more (before I start to look on code). I already have some minor issues I want to optimise.
Let's say you need to parse a string into date and it is localized string (f.x. name of month on local language). In past I would definitely define an array with months and then parse a String to get a number of my month and then build a Date object. In Java it's pretty simple (almost 1 line of code).
Locale approach
package parser;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class parser {
public static void main(String[] args) throws ParseException {
Locale locale = new Locale("ru");
Date date = convertStringToDate("19 Сентября 2004", "d MMMM yyyy", locale);
System.out.println(date); // Sun Sep 19 00:00:00 CEST 2004
}
public static java.util.Date convertStringToDate(String dateString, String format, Locale locale) throws ParseException {
return new SimpleDateFormat("d MMMM yyyy", locale).parse(dateString);
}
}
Alternatively, if you need to define months yourself use DateFormatSymbols
Define symbols in DateFormatSymbols
package parser;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class parser {
public static void main(String[] args) throws ParseException {
String months[] = {"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"};
DateFormatSymbols dfs = new DateFormatSymbols();
dfs.setMonths(months);
SimpleDateFormat sdf = new SimpleDateFormat("d MMMM yyyy");
sdf.setDateFormatSymbols(dfs);
System.out.print(sdf.parse("19 Сентября 2004")); // Sun Sep 19 00:00:00 CEST 2004
}
}
You have a website and want to be sure that there are no Orphan pages? Not sure how to develop it? If so - you found a right place to go :), let's talk about most important steps.
I assume you have a list of all pages you want to verify (otherwise - it will be your first task)
Here is a logic/snippets
We need functionality that can extract HTML from a web page. Later we will scan it and get internal links.
private String getPageContent(String pageurl) throws Exception {
StringBuffer buf = new StringBuffer();
URL url = new URL(pageurl);
InputStream is = url.openConnection().getInputStream();
BufferedReader reader = new BufferedReader( new InputStreamReader( is ) );
String line = null;
while( ( line = reader.readLine() ) != null ) {
buf.append(line);
}
reader.close();
return buf.toString();
}
Make a logic that can deal with DOM. I use jsoup to manipulate with HTML and I really recommend it (easy and fast). The method below select all links that begin with baseurl (it's domain of your website), in that way we can cut all external links and get only internal links.
private List<string> getAllInernalLinks(String html, String baseurl) throws Exception {
List<string> res = new ArrayList<string>();
String select = "a[href^="+baseurl+"]";
org.jsoup.nodes.Document dom = Jsoup.parse(html);
Elements links = dom.select(select);
for (Element link : links) {
String src = link.attr("href");
res.add(src);
}
return res;
}
Now we must build a List with all internal links from all pages on your website.
Recently I faced up with WSDL which I couldn't import into Web Service Consumer. Our consumer worked well from last 5 years but it is a long period and during that time our Service Provider was updated a lot so we decided to update our Consumer as well. Guess everything went fine?
No WSDL was returned from the URL
I simply created new Consumer in Domino Designer, set URL to our WSDL, picked Java and clicked OK. Oops...
---------------------------
Domino Designer
---------------------------
No WSDL was returned from the URL:
https://api.ourserver.com/secure/api1/WebService?WSDL
---------------------------
OK
---------------------------
The requested operation failed: no import files
Wow, thought I :) let's try to import WSDL as Lotus Script then (just to see if it is not related to Java)
---------------------------
IBM Domino Designer
---------------------------
The requested operation failed: no import files
---------------------------
OK
---------------------------
Name too long
Hey, what? This WSDL is used by many another applications without any issues, what is going on!? I downloaded WSDL as file to my local PC and tried to import it as Lotus Script again. This time it went fine (except issues with Name too long). Well, great news anyway, at least everything works when WSDL is a local file.
The Web Service implementation code generated from the provided WSDL could not be compiled, so no design element was created
Ok, it worked for Lotus Script, let's set now Java...
---------------------------
IBM Domino Designer
---------------------------
The Web Service implementation code generated from the provided WSDL could not be compiled, so no design element was created. Please correct the WSDL and try again. The errors are located in the following file:: C:\Users\dpa\AppData\Local\Temp\notes90C43B\47238811.err
---------------------------
OK
---------------------------
OK, it's time to blame Designer and IBM! Why it is so difficult just to import WSDL? All another application that use WSDL from our server did not have such issues. It's just not fair :). Found a file with error and quite typical line: java.lang.OutOfMemoryError: Java heap space. I knew what to do, I increased HTTPJVMMaxHeapSize and JavaMaxHeapSize to 512M, restarted Designer/Notes and tried again. Worked well! I restored original values to HTTPJVMMaxHeapSize and JavaMaxHeapSize after that.
The system is out of resources.
Consult the following stack trace for details.
java.lang.OutOfMemoryError: Java heap space
at com.sun.tools.javac.util.Position$LineMapImpl.build(Position.java:151)
at com.sun.tools.javac.util.Position.makeLineMap(Position.java:75)
at com.sun.tools.javac.parser.Scanner.getLineMap(Scanner.java:1117)
at com.sun.tools.javac.main.JavaCompiler.parse(JavaCompiler.java:524)
at com.sun.tools.javac.main.JavaCompiler.parse(JavaCompiler.java:562)
at com.sun.tools.javac.main.JavaCompiler.parseFiles(JavaCompiler.java:816)
at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:739)
at com.sun.tools.javac.main.Main.compile(Main.java:365)
at com.sun.tools.javac.main.Main.compile(Main.java:291)
at com.sun.tools.javac.main.Main.compile(Main.java:282)
at com.sun.tools.javac.Main.compile(Main.java:99)
at lotus.notes.internal.IDEHelper.compile(Unknown Source)
Simple thing however it costed few hours for me. Hope it will save some time for other people.