EN4J
What is it?
-----------
EN4J is a client to the Evernote services written in Java. It designed to
become a replacement to the Evernote client for Windows & Mac. Although
currently EN4J is a read-only client and cannot fully replace the
official Evernote client.
Features
--------
* Fast search based on Lucene
* Indexing of word documents, zip files, etc. All doc types supported by Apache Tika
* Based on Netbeans Platform, runs on Windows, Mac OS X and Linux
* Open source
TO DO
-----
* Editable notes
* tags
* Keyring api
* Rich-text clipboard copy
* Ignore diacritical marks during search
Bugs
----
If you find any write a bug report on http://github.com/ecerulm/en4j/issues
How to build and install
------------------------
First, grab the source from github
git clone git://github.com/ecerulm/en4j.git
Then proceed to the main directory
cd en4j/NBPlatformApp
to build the windows zip distribution do
ant build-zip
to build the mac os x distribution do
ant build-mac
Then take the output file in NBPlatformApp/dist
Contribute
----------
Just fork the project from http://github.com/ecerulm/en4j
and send me a pull request with the changes.
Licensing
---------
Please see the file called LICENSE.
Credits
-------
Netbeans team for the Netbeans Plaform and Netbeans IDE
icons: Tango Icon Library http://tango.freedesktop.org/Tango_Icon_Library
icons: Nuvola http://www.icon-king.com/projects/nuvola/
icons: Crystal project http://www.everaldo.com/crystal/
icons: Addictive Flavour. Designed by Mirjami Manninen (http://www.mirkku.com). www.smashingmagazine.com
icons: Bunch of Cool Bluish ICONS http://mebaze.com/
GlazedLists: http://publicobject.com/glazedlists/
Apache Thrift: http://incubator.apache.org/thrift/
TimingFramework: https://timingframework.dev.java.net/
Flying Saucer: https://xhtmlrenderer.dev.java.net/
Apache Tika: http://tika.apache.org/
Apache Commons Collections: http://commons.apache.org/collections/
Apache Commons IO: http://commons.apache.org/io/
Apache Commons Math: http://commons.apache.org/math/
Evernote API: http://www.evernote.com/about/developer/api/
Joda Time library: http://joda-time.sourceforge.net/
NekoHTML: http://nekohtml.sourceforge.net/
renderpack: https://renderpack.dev.java.net/
SLF4J: http://www.slf4j.org
Woodstox: http://woodstox.codehaus.org/
I took me a while to figure out how to rewind the stack while debugging in Netbeans. This feature is called “Drop to Frame” in Eclipse and there is accesible in the toolbar. In Netbeans the feature that allows you to go back in the callstack is called Pop to Here and you invoke it right clicking on the stack. There is also a “Debug / Stack / Pop Topmost Call”
in my project.properties I got a NullPointerException (NPE) at startup. I though it was a bug and I reported it but then I realized that I got the NPE because I had removed/hidden also the editor.wsmode in my layer.xml
The short answer is that the gray area that you are seeing in lower bottom of the window is actually the editor area (aka document area) and you can hide it by adding
That’s all you need to know but if you are interested on getting some details and references continue reading.
If you create a new Netbeans RCP application and only add TopComponents to modes other than editor (modes which kind=“view”) then you will end up with an empty document area (could be at the bottom, top, left, right or in between TopComponents depending which modes you placed the TCs on). But first, you ask, how do I assign TopComponents to modes? Well you do that in the new Window wizard for example
Window Position is actually setting the mode (output, properties, explorer, editor) where the TopComponent will land. You can also do the same in the layer.xml file. See more on modes in theseposts.
This grey area that we are talking about… how big it is? and why should it care? Well it not that big from the start but if you resize the window probably it will get noticeable big.
For example this is my test app at startup (the grey area is at the middle)
… to another recently reported and fixed bug where IndexWriter held references to Readers used in your Fields, (and if you have apache tika’s reader, those can take up a lot of space)
If you need to unit test classes in a netbeans module and the classes use the Lookup.getDefault() to “lookup” things that you want to mock you probably wonder how you can place the mock objects in the default Lookup
Adding objects to the Netbeans’s default Lookup is relatively simple. Here is the answer:
Replace the Lookup instance returned by the Lookup.getDefault()
by defining a new entry in META-INF/services.
It’s just a matter of adding a file named org.openide.util.Lookup$Provider in the META-INF/services folder inside your Unit Test source folder. That file should contain the fully qualified class name of your own Lookup.Provider implementation.
In my case the file contain just the string com.rubenlaguna.en4j.searchlucene.NoteFinderLuceneImplTest because the test class itself implements Lookup.Provider.
By adding this to META-INF/services you’re actually forcing Lookup.getLookup() to use your Lookup.Provider to obtain the Lookup instance that will be returned
Create you own Lookup.Provider. You can even let your test class implement Lookup.Provider if you only have one test class.
In this example you can see that I inject a NoteRepository.class into the default lookup.
If you want to access the internet via your phone 3G/GPRS connection you can use Bluetooth PAN. In my case I have a Sony Ericsson W715 so the first thing to do is to tell the phone which connection to use Menu > Settings > Connectivity > Bluetooth > Data accounts and select the account that you normally use to browse the net from the phone.
Then turn on bluetooth
Then in the mac:
Go to System Preferences > Bluetooth
Set up new device.
Set up a new device
Click on continue
Then a screen will appear with a passcode that you have to enter in your phone to do the pairing. if the phone ask you if you want to start the remote control say yes.
In the next screen enter your phone model and apn in my case its online.telia.se and he cid i used 5 because i think itsd unused
See summary and click quit
Go to System preferences > Network
There is a “Bluetooth PAN” already but that one won’t work: you need to create a new connection
Press the “+” in the bottom left corner.
Set the interface to “Bluetooth PAN”
Set the name of the connection to whatever you like. I just typed “W715” in my case.
Turn off Airport or any other connection that you may be using and the go to your newly created connection and click on “Connect”
Progress indicator (indeterminate mode) which allows to cancel the task
The easiest way of having a cancellable progress indicator (Progress API) for a task in Netbeans Platform Application is the one below but it’s only worth for tasks that don’t update the progress indicator (indeterminate mode) until the task is finished. If you want to update the progress indicator (ProgressHandle) inside the task itself look at the second example.
<span class='line'>package com.rubenlaguna.en4j.mainmodule;
</span><span class='line'>
</span><span class='line'>import java.awt.event.ActionEvent;
</span><span class='line'>import java.awt.event.ActionListener;
</span><span class='line'>import java.util.logging.Logger;
</span><span class='line'>import org.netbeans.api.progress.ProgressHandle;
</span><span class='line'>import org.netbeans.api.progress.ProgressHandleFactory;
</span><span class='line'>import org.openide.util.RequestProcessor;
</span><span class='line'>import org.openide.util.TaskListener;
</span><span class='line'>
</span><span class='line'>public final class MyCancellableActionNoProgressInformation implements ActionListener {
</span><span class='line'>
</span><span class='line'> //The RequestProcessor has to have allowInterrupt set to true!!
</span><span class='line'> private final static RequestProcessor RP = new RequestProcessor("interruptible tasks", 1, true);
</span><span class='line'> private final static Logger LOG = Logger.getLogger(MyCancellableActionNoProgressInformation.class.getName());
</span><span class='line'>
</span><span class='line'> public void actionPerformed(ActionEvent e) {
</span><span class='line'>
</span><span class='line'> Runnable runnable = new Runnable() {
</span><span class='line'>
</span><span class='line'> private final int NUM = 60000;
</span><span class='line'>
</span><span class='line'> public void run() {
</span><span class='line'> for (int i = 0; i < NUM; i++) {
</span><span class='line'> doSomething(i);
</span><span class='line'> if (Thread.interrupted()) {
</span><span class='line'> LOG.info("the task was CANCELLED");
</span><span class='line'> return;
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> private void doSomething(int i) {
</span><span class='line'> LOG.info("doSomething with " + i);
</span><span class='line'> return;
</span><span class='line'> }
</span><span class='line'> };
</span><span class='line'>
</span><span class='line'> final RequestProcessor.Task theTask = RP.create(runnable);
</span><span class='line'>
</span><span class='line'> final ProgressHandle ph = ProgressHandleFactory.createHandle("performing some task", theTask);
</span><span class='line'> theTask.addTaskListener(new TaskListener() {
</span><span class='line'> public void taskFinished(org.openide.util.Task task) {
</span><span class='line'> //make sure that we get rid of the ProgressHandle
</span><span class='line'> //when the task is finished
</span><span class='line'> ph.finish();
</span><span class='line'> }
</span><span class='line'> });
</span><span class='line'>
</span><span class='line'> //start the progresshandle the progress UI will show 500s after
</span><span class='line'> ph.start();
</span><span class='line'>
</span><span class='line'> //this actually start the task
</span><span class='line'> theTask.schedule(0);
</span><span class='line'> }
</span><span class='line'>}</span>
leads to
INFO [com.rubenlaguna.en4j.mainmodule.ImportEvernoteFile]: doSomething with 6619
INFO [com.rubenlaguna.en4j.mainmodule.ImportEvernoteFile]: doSomething with 6620
INFO [com.rubenlaguna.en4j.mainmodule.ImportEvernoteFile]: doSomething with 6621
INFO [com.rubenlaguna.en4j.mainmodule.ImportEvernoteFile]: doSomething with 6622
INFO [com.rubenlaguna.en4j.mainmodule.ImportEvernoteFile]: the task was CANCELLED
Progress indicator (determinate mode) which allows to cancel the task
This example show a ProgressHandle that it’s updated from the Runnable and it’s stil cancelable. In this case you are also somewhat to use the more cumbersome catch(InterruptedException) instead of just checking Thread.interrupted(). For some reason I couldn’t get the Thread.interrupted() to work in this case. Doing a Thread.sleep(0) will always throw a InterruptedException if the thread was interrupted (canceling a task will interrupt the thread if the RequestProcessor is created with the interruptThread=true).
<span class='line'>package com.rubenlaguna.en4j.mainmodule;
</span><span class='line'>
</span><span class='line'>import java.awt.event.ActionEvent;
</span><span class='line'>import java.awt.event.ActionListener;
</span><span class='line'>import java.util.logging.Logger;
</span><span class='line'>import org.netbeans.api.progress.ProgressHandle;
</span><span class='line'>import org.netbeans.api.progress.ProgressHandleFactory;
</span><span class='line'>import org.openide.util.Cancellable;
</span><span class='line'>import org.openide.util.RequestProcessor;
</span><span class='line'>import org.openide.util.Task;
</span><span class='line'>import org.openide.util.TaskListener;
</span><span class='line'>
</span><span class='line'>public final class MyCancellableAction implements ActionListener {
</span><span class='line'>
</span><span class='line'> private final static RequestProcessor RP = new RequestProcessor("interruptible tasks", 1, true);
</span><span class='line'> private final static Logger LOG = Logger.getLogger(MyCancellableAction.class.getName());
</span><span class='line'> private RequestProcessor.Task theTask = null;
</span><span class='line'>
</span><span class='line'> public void actionPerformed(ActionEvent e) {
</span><span class='line'> final ProgressHandle ph = ProgressHandleFactory.createHandle("task thats shows progress", new Cancellable() {
</span><span class='line'>
</span><span class='line'> public boolean cancel() {
</span><span class='line'> return handleCancel();
</span><span class='line'> }
</span><span class='line'> });
</span><span class='line'>
</span><span class='line'> Runnable runnable = new Runnable() {
</span><span class='line'>
</span><span class='line'> private final int NUM = 60000;
</span><span class='line'>
</span><span class='line'> public void run() {
</span><span class='line'> try {
</span><span class='line'> ph.start(); //we must start the PH before we swith to determinate
</span><span class='line'> ph.switchToDeterminate(NUM);
</span><span class='line'> for (int i = 0; i < NUM; i++) {
</span><span class='line'> doSomething(i);
</span><span class='line'> ph.progress(i);
</span><span class='line'> Thread.sleep(0); //throws InterruptedException is the task was cancelled
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> } catch (InterruptedException ex) {
</span><span class='line'> LOG.info("the task was CANCELLED");
</span><span class='line'> return;
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> private void doSomething(int i) {
</span><span class='line'> LOG.info("doSomething with " + i);
</span><span class='line'> return;
</span><span class='line'> }
</span><span class='line'> };
</span><span class='line'>
</span><span class='line'> theTask = RP.create(runnable); //the task is not started yet
</span><span class='line'>
</span><span class='line'> theTask.addTaskListener(new TaskListener() {
</span><span class='line'> public void taskFinished(Task task) {
</span><span class='line'> ph.finish();
</span><span class='line'> }
</span><span class='line'> });
</span><span class='line'>
</span><span class='line'> theTask.schedule(0); //start the task
</span><span class='line'>
</span><span class='line'>
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> private boolean handleCancel() {
</span><span class='line'> LOG.info("handleCancel");
</span><span class='line'> if (null == theTask) {
</span><span class='line'> return false;
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> return theTask.cancel();
</span><span class='line'> }
</span><span class='line'>}</span>
leads to
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5399
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5400
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5401
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5402
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5403
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5404
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5405
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: handleCancel
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: doSomething with 5406
INFO [com.rubenlaguna.en4j.mainmodule.MyCancellableAction]: the task was CANCELLED
I’m starting to get tired of OpenJPA/HSQLDB setup that I’ve trying: it seems to give more problems than it solves. Now I discovered that the HSQL db wasn’t been properly shutdown and for reasons that I’m investigating, if the HSQL db is not propertly closed (SHUTDOWN command) it’s impossible to reopen it again. It fails with a IndexOutOfBoundsException.
Annoying. This is not OpenJPA fault, but it gets complicated to actually solve it.
The obvious solution was to add a “;shutdown=true” to the openjpa.ConnectionURL property. That works but it makes the database access unbearable slow. Presumably because it makes HSQL to perform a SHUTDOWNeach time OpenJPA closes the connection, and I guess that OpenJPA is closing the connection every couple of INSERTs. This is a guess, I couldn’t confirm it but I see a lot of GC (garbage collection) going on between INSERT and INSERT and I though that I could be that.
UPDATE: Now I’m sure that OpenJPA is closing the connection, I can see it in the HSQL logs. There is a way to enable connection pooling in OpenJPA although is non-trivial.
So “;shutdown=true” is not feasible right now, unless there is something that I could do to raise HSQLDB performance to an acceptable level while using this option.
I was forced to issue the SHUTDOWN myself. In my case in close() in a Netbean’s ModuleInstaller. Which I still don’t like because if the application is not propertly closed itself, the DB won’t be SHUTDOWN and the database will be unreadable again.
<span class='line'>public class Installer extends ModuleInstall {
</span><span class='line'>
</span><span class='line'> private static EntityManagerFactory EMF = null;
</span><span class='line'> private static EntityManager EM = null;
</span><span class='line'> private static String connectionURL = null;
</span><span class='line'> private static final Logger LOG = Logger.getLogger(Installer.class.getName());
</span><span class='line'>
</span><span class='line'> @Override
</span><span class='line'> public void close() {
</span><span class='line'> if (null != EM) {
</span><span class='line'> //things get cumbersome. We must syncronize access to EM.
</span><span class='line'> //here and anywhere else where it's used
</span><span class='line'> synchronized (EM) {
</span><span class='line'> EM.close(); //we know that there is no active transaction
</span><span class='line'> //running because we synchronized the access
</span><span class='line'> //and all the other synchonized blocks
</span><span class='line'> //never leave the transaction open
</span><span class='line'> LOG.info("closed EntityManager " + EM);
</span><span class='line'>
</span><span class='line'> //Issue a SHUTDOWN command via JPA Native Query
</span><span class='line'> final EntityManager emanager = EMF.createEntityManager();
</span><span class='line'> LOG.info("JPA Native Query shutdown");
</span><span class='line'> emanager.getTransaction().begin();
</span><span class='line'> emanager.createNativeQuery("SHUTDOWN").executeUpdate();
</span><span class='line'> emanager.getTransaction().commit();
</span><span class='line'> emanager.close();
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'> if (null != EMF) {
</span><span class='line'> LOG.info("closing EntityManagerFactory " + EMF);
</span><span class='line'> EMF.close();
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> public static EntityManager getEntityManager() {
</span><span class='line'> if (EMF == null) {
</span><span class='line'> Map properties = new HashMap();
</span><span class='line'> connectionURL = "jdbc:hsqldb:file:" + System.getProperty("netbeans.user") + "/en4j/db";
</span><span class='line'> properties.put("openjpa.ConnectionURL", connectionURL);
</span><span class='line'> EMF = javax.persistence.Persistence.createEntityManagerFactory("JpaEntitiesClassLibraryPU", properties);
</span><span class='line'> }
</span><span class='line'>
</span><span class='line'> if (null == EM) {
</span><span class='line'> EM = EMF.createEntityManager();
</span><span class='line'> }
</span><span class='line'> synchronized (EM) {
</span><span class='line'> return java.beans.Beans.isDesignTime() ? null : EM;
</span><span class='line'> }
</span><span class='line'> }
</span><span class='line'>}</span>
As I said the everything gets more complicated because know the entityManager could be accessed simultaneosly by two different threads. The ModuleInstall.close() get invoked from a different thread and suddenly we have to use synchronize blocks arround the EntityManager and check that the EntityManager is still open (with EntityManager.isOpen()) everywhere.