Loading

Building Apache Tika 0.6 fails if the locale is not en_US

I tried to build Apache Tika 0.6 yesterday and I couldn’t build it because the tests failed. The failing tests were

testExcelParserFormatting(org.apache.tika.parser.microsoft.ExcelParserTest)
testExcelFormats(org.apache.tika.parser.microsoft.ooxml.OOXMLParserTest)

and the failure had to to with the fact that the locale was “es_ES” and the numbering format differs (“1.599,99” and not “1,599.99“)

$ mvn -version
Apache Maven 2.2.0 (r788681; 2009-06-26 15:04:01+0200)
Java version: 1.6.0_17
Java home: /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
Default locale: es_ES, platform encoding: MacRoman
OS name: "mac os x" version: "10.6.2" arch: "x86_64" Family: "mac"

I changed the locale temporarily to be able to build

export LC_ALL=en_US.UTF-8

so those test no longer failed. But then I run into OutOfMemoryError while running some of the tests so I set MAVEN_OPTS:

export MAVEN_OPTS="-Xmx2048m"

And then mvn install suceeded !. I got the jars
find . -name "*jar"
./tika-app/target/tika-app-0.6.jar
./tika-bundle/target/tika-bundle-0.6.jar
./tika-core/target/tika-core-0.6.jar
./tika-parsers/target/tika-parsers-0.6.jar

JUnit and Netbeans. Injecting in objects in the default Lookup

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:

  1. 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

  2. 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.

You can see a living example here

Connect to the internet via Bluetooth PAN with Mac OS X Snow Leopard

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:

  1. Go to System Preferences > Bluetooth

  2. Set up new device.

  3. Set up a new device
  4. 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.

  5. 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

  6. Go to System preferences > Network
  7. 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.

  8. Turn off Airport or any other connection that you may be using and the go to your newly created connection and click on “Connect”

Cancellable tasks and progress indicators [Netbeans Platform]

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.

ProgressHandle indeterminate

ProgressHandle indeterminate

package com.rubenlaguna.en4j.mainmodule;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Logger;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.RequestProcessor;
import org.openide.util.TaskListener;

public final class MyCancellableActionNoProgressInformation implements ActionListener {

    //The RequestProcessor has to have  allowInterrupt set to true!!
    private final static RequestProcessor RP = new RequestProcessor("interruptible tasks", 1, true);
    private final static Logger LOG = Logger.getLogger(MyCancellableActionNoProgressInformation.class.getName());

    public void actionPerformed(ActionEvent e) {

        Runnable runnable = new Runnable() {

            private final int NUM = 60000;

            public void run() {
                for (int i = 0; i < NUM; i++) {
                    doSomething(i);
                    if (Thread.interrupted()) {
                        LOG.info("the task was CANCELLED");
                        return;
                    }
                }

            }

            private void doSomething(int i) {
                LOG.info("doSomething with " + i);
                return;
            }
        };

        final RequestProcessor.Task theTask = RP.create(runnable);

        final ProgressHandle ph = ProgressHandleFactory.createHandle("performing some task", theTask);
        theTask.addTaskListener(new TaskListener() {
            public void taskFinished(org.openide.util.Task task) {
                //make sure that we get rid of the ProgressHandle
                //when the task is finished
                ph.finish();
            }
        });

        //start the progresshandle the progress UI will show 500s after
        ph.start();

        //this actually start the task
        theTask.schedule(0);
    }
}
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 still cancellable. 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 (cancelling a task will interrupt the thread if the RequestProcessor is created with the interruptThread=true).

Progress indicator (determinate mode)

Progress indicator (determinate mode)

package com.rubenlaguna.en4j.mainmodule;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Logger;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.openide.util.Cancellable;
import org.openide.util.RequestProcessor;
import org.openide.util.Task;
import org.openide.util.TaskListener;

public final class MyCancellableAction implements ActionListener {

    private final static RequestProcessor RP = new RequestProcessor("interruptible tasks", 1, true);
    private final static Logger LOG = Logger.getLogger(MyCancellableAction.class.getName());
    private RequestProcessor.Task theTask = null;

    public void actionPerformed(ActionEvent e) {
        final ProgressHandle ph = ProgressHandleFactory.createHandle("task thats shows progress", new Cancellable() {

            public boolean cancel() {
                return handleCancel();
            }
        });

        Runnable runnable = new Runnable() {

            private final int NUM = 60000;

            public void run() {
                try {
                    ph.start(); //we must start the PH before we swith to determinate
                    ph.switchToDeterminate(NUM);
                    for (int i = 0; i < NUM; i++) {
                        doSomething(i);
                        ph.progress(i);
                        Thread.sleep(0); //throws InterruptedException is the task was cancelled
                    }

                } catch (InterruptedException ex) {
                    LOG.info("the task was CANCELLED");
                    return;
                } 

            }

            private void doSomething(int i) {
                LOG.info("doSomething with " + i);
                return;
            }
        };

        theTask = RP.create(runnable); //the task is not started yet

        theTask.addTaskListener(new TaskListener() {
            public void taskFinished(Task task) {
                ph.finish();
            }
        });

        theTask.schedule(0); //start the task

    }

    private boolean handleCancel() {
        LOG.info("handleCancel");
        if (null == theTask) {
            return false;
        }

        return theTask.cancel();
    }
}
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

OpenJPA, HSQLDB and proper shutdown

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.

Caused by: org.hsqldb.HsqlException: error in script file line: 33 java.io.IOException: java.lang.IndexOutOfBoundsException in statement [SET TABLE PUBLIC.NOTES INDEX '2883070']
at org.hsqldb.Error.error(Error.java:111)
at org.hsqldb.scriptio.ScriptReaderText.readDDL(ScriptReaderText.java:132)
at org.hsqldb.scriptio.ScriptReaderBase.readAll(ScriptReaderBase.java:88)
at org.hsqldb.persist.Log.processScript(Log.java:721)
at org.hsqldb.persist.Log.open(Log.java:187)
at org.hsqldb.persist.Logger.openPersistence(Logger.java:209)
at org.hsqldb.Database.reopen(Database.java:265)
at org.hsqldb.Database.open(Database.java:235)
at org.hsqldb.DatabaseManager.getDatabase(DatabaseManager.java:222)
at org.hsqldb.DatabaseManager.newSession(DatabaseManager.java:145)
at org.hsqldb.jdbc.JDBCConnection.<init>(JDBCConnection.java:3219)
... 59 more

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 SHUTDOWN each 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. 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.

public class Installer extends ModuleInstall {

    private static EntityManagerFactory EMF = null;
    private static EntityManager EM = null;
    private static String connectionURL = null;
    private static final Logger LOG = Logger.getLogger(Installer.class.getName());

    @Override
    public void close() {
        if (null != EM) {
            //things get cumbersome. We must syncronize access to EM.
            //here and anywhere else where it's used
            synchronized (EM) {
                EM.close();  //we know that there is no active transaction
                             //running because we synchronized the access
                             //and all the other synchonized blocks
                             //never leave the transaction open
                LOG.info("closed EntityManager " + EM);

                //Issue a SHUTDOWN command via JPA Native Query
                final EntityManager emanager = EMF.createEntityManager();
                LOG.info("JPA Native Query shutdown");
                emanager.getTransaction().begin();
                emanager.createNativeQuery("SHUTDOWN").executeUpdate();
                emanager.getTransaction().commit();
                emanager.close();
            }
        }
        if (null != EMF) {
            LOG.info("closing EntityManagerFactory " + EMF);
            EMF.close();
        }

    }

    public static EntityManager getEntityManager() {
        if (EMF == null) {
            Map properties = new HashMap();
            connectionURL = "jdbc:hsqldb:file:" + System.getProperty("netbeans.user") + "/en4j/db";
            properties.put("openjpa.ConnectionURL", connectionURL);
            EMF = javax.persistence.Persistence.createEntityManagerFactory("JpaEntitiesClassLibraryPU", properties);
        }

        if (null == EM) {
            EM = EMF.createEntityManager();
        }
        synchronized (EM) {
            return java.beans.Beans.isDesignTime() ? null : EM;
        }
    }
}

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 synchonize blocks arround the EntityManager and check that the EntityManager is still open (with EntityManager.isOpen()) everywhere.

JTable bound to a database with lazy loading

I’ve been doing experiments with JTables bound to database tables in the past. But I was not satisfied with the result to be honest. The pagination helps but there is still noticeable pauses each time a database query has to be issued. So I started looking into other possibilities.

I come up with the idea of doing lazy loading and presenting some fake data until the data is really retrieved from the database, I did a small proof of concept and seems to work ok.

The solution goes like this. It uses Beans Binding to bind a JTable to a custom List like in the previous post. This custom list (ResultListDerby in the example below) returns Beans (the Bean is called Customer in the example) that are initially empty. By empty I mean that all its properties return something like “<data not retrieved from the database yet>“. So the JTable will be initially full of “<data not retrieved yet>“.

The picture above shows the JTable showing the List of Customers. Each row represents a Customer instance.

Having empty/fake Customer beans allow a fast drawing to screen but it’s not very useful. Now, if at the same point as each Customer is created we create SwingWorker and schedule it to run in a Executor then we can get something more meaningful. So what we do in the SwingWorker? The SwingWorker, you guessed it, will retrieve the data from the database and update the bean (look at ResultListDerby.getItem() method). When the bean (Customer.java) is updated it will notify its listeners of the changes, automatically by means of bound properties , in this case JTableBinding will be notified and the JTable that will repaint the cells to reflect the changes in the Bean. (Check how bound properties are implemented with of PropertyChangeSupport in Customer.java below). The rows will be updated one by one, as soon as the Executor completes a SwingWorker task a row will be updated.

This solution is free from annoying GUI freezes. The beans are always created fast because there is no need to go to the database to create them and that keeps the GUI liveness high. By moving the database access to SwingWorkers we keep the EDT (event dispatch thread) lightweight. The solution also avoids having too many running SwingWorkers at the same time by using a ExecutorService instead of executing them directly via the SwingWorker.execute() method (Check ResultListDerby.getItem() to see how it’s done).

package jtablepagination;

import java.lang.ref.WeakReference;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.AbstractList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingWorker;

/**
 *
 * @author Ruben Laguna <ruben.laguna at gmail.com>
 */
public class MainWindow extends javax.swing.JFrame {

    /** Creates new form MainWindow */
    public MainWindow() {
        initComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {
        bindingGroup = new org.jdesktop.beansbinding.BindingGroup();

        list1 = getList();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        org.jdesktop.swingbinding.JTableBinding jTableBinding = org.jdesktop.swingbinding.SwingBindings.createJTableBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, list1, jTable1);
        org.jdesktop.swingbinding.JTableBinding.ColumnBinding columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${id}"));
        columnBinding.setColumnName("id");
        columnBinding.setColumnClass(Integer.class);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${firstName}"));
        columnBinding.setColumnName("first name");
        columnBinding.setColumnClass(String.class);
        columnBinding = jTableBinding.addColumnBinding(org.jdesktop.beansbinding.ELProperty.create("${lastName}"));
        columnBinding.setColumnName("last name");
        columnBinding.setColumnClass(String.class);
        bindingGroup.addBinding(jTableBinding);
        jTableBinding.bind();
        jScrollPane1.setViewportView(jTable1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 344, Short.MAX_VALUE)
                .addContainerGap())
        );

        bindingGroup.bind();

        pack();
    }// </editor-fold>                        

    private List<Customer> getList() {
        List<Customer> toReturn = new ResultListDerby();
        return toReturn;
    }

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainWindow().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
    private java.util.List<Customer> list1;
    private org.jdesktop.beansbinding.BindingGroup bindingGroup;
    // End of variables declaration                   

}

 class ResultListDerby extends AbstractList {

    private Connection connection;
    private final ExecutorService ex = Executors.newSingleThreadExecutor();
    private int size = -1;
    //maintain a cache with the Customer instances already created and alive
    private Map<Integer, WeakReference<Customer>> cache = new HashMap<Integer, WeakReference<Customer>>();

    ResultListDerby() {
        try {
            Class.forName("org.apache.derby.jdbc.ClientDriver");
            this.connection = DriverManager.getConnection("jdbc:derby://localhost:1527/customer", "nbuser", "nbuser");
        } catch (Exception ex) {
            Logger.getLogger(ResultListDerby.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
    }

    public int size() {
        if (this.size == -1) {
            try {
                final ResultSet resultset = connection.createStatement().executeQuery("SELECT COUNT(ID) FROM CUSTOMERS");
                resultset.next();
                final int toReturn = resultset.getInt(1);
                this.size = toReturn;
            } catch (SQLException ex) {
                Logger.getLogger(ResultListDerby.class.getName()).log(Level.SEVERE, null, ex);
                throw new RuntimeException(ex);
            }
        }
        return this.size;
    }

    public Customer get(int rowIndex) {
        //this way we ensure that we don't create several Customer instances
        //for the same id. Otherwise it would be confusing for beansbindings.
        Customer toReturn = null;
        if (null != this.cache.get(rowIndex)) {
            toReturn = this.cache.get(rowIndex).get();
        }
        if (null == toReturn) {
            toReturn = getItem(rowIndex);
            this.cache.put(rowIndex, new WeakReference<Customer>(toReturn));
        }

        return toReturn;
    }

    private Customer getItem(final int j) {
        final Customer customer = new Customer(j);

        Runnable task = new SwingWorker() {

            private String firstNameValue;
            private String lastNameValue;

            @Override
            protected Object doInBackground() throws Exception {
                //this is always executed in a different thread from the current thread
                //it doesn't matter if the current thread is the EDT or a thread in the Executor
                final java.sql.Statement stmt = connection.createStatement();
                ResultSet executeQuery = stmt.executeQuery("SELECT ID, FIRST_NAME,LAST_NAME FROM CUSTOMERS ORDERDESC OFFSET " + j + " ROWS FETCH NEXT ROWS ONLY");
                executeQuery.next();
                firstNameValue = executeQuery.getString(2);
                lastNameValue = executeQuery.getString(3);
                return null;
            }

            @Override
            protected void done() {
                //this in the other hand will always be executed on the EDT.
                //This has to be done in the EDT because currently JTableBinding
                //is not smart enough to realize that the notification comes in another
                //thread and do a SwingUtilities.invokeLater. So we are force to execute this
                // in the EDT. Seee http://markmail.org/thread/6ehh76zt27qc5fis and
                // https://beansbinding.dev.java.net/issues/show_bug.cgi?id=60

                customer.setFirstName(firstNameValue);
                customer.setLastName(lastNameValue);
                Logger.getLogger(ResultListDerby.class.getName()).info("updating customer " + customer);
            }
        };

        //NOTE that we don do task.execute()
        //posting the task to an Executor gives more control on
        //how many threads are created.
        ex.execute(task);
        return customer;
    }
}
package jtablepagination;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.logging.Logger;

/**
 *
 * @author Ruben Laguna <ruben.laguna at gmail.com>
 */
public class Customer {

    private static final Logger LOG = Logger.getLogger(Customer.class.getName());
    private static final String NOT_RETRIEVED_YET = "<not retrieved yet>";
    private int id = 0;
    public static final String PROP_ID = "id";
    protected String firstName = NOT_RETRIEVED_YET;
    public static final String PROP_FIRSTNAME = "firstName";
    protected String lastName = NOT_RETRIEVED_YET;
    public static final String PROP_LASTNAME = "lastName";
    private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    Customer(int j) {
        this.id = j;
    }

    public int getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        String oldFirstName = this.firstName;
        this.firstName = firstName;
        propertyChangeSupport.firePropertyChange(PROP_FIRSTNAME, oldFirstName, firstName);
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        String oldLastName = this.lastName;
        this.lastName = lastName;
        if (propertyChangeSupport.hasListeners(PROP_LASTNAME)) {
            LOG.info("notifying!!!");
        } else {
            LOG.info("there is no listeners for the property");
        }

        propertyChangeSupport.firePropertyChange(PROP_LASTNAME, oldLastName, lastName);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        LOG.info("add listener to customer " + this.id);
        propertyChangeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.removePropertyChangeListener(listener);
    }

    @Override
    public String toString() {
        return "id: " + id + " firstName: " + firstName;
    }
}

References

Migrating from Blinklist to Delicious.com: CSV to HTML

Apparently blinklist doesn’t export bookmarks to JSON format any longer and delicious has changed authentication scheme for its delicious API for new accounts (now it forces new users to use the OAuth / Yahoo ID). So the solutions described in this old post of mine doesn’t work.

So given the current state of affairs the only way to get your bookmarks out of Blinklist is CSV (actually tab-separated) and the only easy way to import them to delicious is to use the HTML import. So we need a way to transforms Blinklist’s CSV to HTML bookmark file format. So I created this ruby scripts that takes bookmark.csv and generates bookmarks.html that you can import to delicious.

#!/usr/bin/ruby
require "rubygems"
require "csv" 

i=0
File.open('bookmarks.html', 'w') do |f|
  f.print <<EOF
  <!DOCTYPE NETSCAPE-Bookmark-file-1>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
  <!-- This is an automatically generated file.
  It will be read and overwritten.
  Do Not Edit! -->
  <TITLE>Bookmarks</TITLE>
  <H1>Bookmarks</H1>
  <DL><p>
EOF

  CSV.open('bookmark.csv','r',"\t") do  |item|
    next if item[0] == "url"
    i += 1
    puts "#{i}: #{item[0]}" #the url is position 0
    puts "#{i}: #{item[1]}" #the name is position 1
    puts "#{i}: #{item[3]}" #the tags are in position 3
    #next if i > 3229
    #r.add(item[0],item[1],"no description", getTags(item), getTime(item), true, getIsPrivate(item)) #url, name,tags,time,
    f.puts "<DT><A HREF=\"#{item[0]}\" LAST_VISIT=\"1248434357\" ADD_DATE=\"1248434357\" TAGS=\"#{item[3]}\">#{item[1]}</A>"
  end
  f.puts "</DL><p>"
end
puts "ended";

It seems that in newer version of ruby there are changes in the csv module

#!/usr/bin/ruby
require "rubygems"
require "csv" 

i=0
File.open('bookmarks.html', 'w') do |f|
  f.print <<EOF
  <!DOCTYPE NETSCAPE-Bookmark-file-1>
  <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
  <!-- This is an automatically generated file.
  It will be read and overwritten.
  Do Not Edit! -->
  <TITLE>Bookmarks</TITLE>
  <H1>Bookmarks</H1>
  <DL><p>
EOF

  CSV.foreach('bookmark.csv',{:col_sep => "\t"}) do  |item|
    next if item[0] == "url"
    i += 1
    puts "#{i}: #{item[0]}" #the url is position 0
    puts "#{i}: #{item[1]}" #the name is position 1
    puts "#{i}: #{item[2]}" #the description is in position 2
    puts "#{i}: #{item[3]}" #the tags are in position 3
    #next if i > 3229
    #r.add(item[0],item[1],"no description", getTags(item), getTime(item), true, getIsPrivate(item)) #url, name,tags,time,
    f.puts "<DT><A HREF=\"#{item[0]}\" LAST_VISIT=\"1248434357\" ADD_DATE=\"1248434357\" TAGS=\"#{item[3]}\">#{item[1]}</A>
<DD>#{item[2]}"
  end
  f.puts "</DL><p>"
end
puts "ended";

Two netbeans bugs at the same time

It seems that source attachement for Library Wrapper Modules in Netbeans 6.8 Beta doesn’t work properly. I opened a bug report here https://netbeans.org/bugzilla/show_bug.cgi?id=176800#c9 and it seems that in the process I also spotted that “Swing Layout Extensions” library definition that comes with NB 6.8 it’s also wrong (see https://netbeans.org/bugzilla/show_bug.cgi?id=176800#c7)

How to handle custom tags using Flying Saucer

If you have a customized xhtml variant, like evernote ENML format and you want to render it with Flying Saucer R8 you must first make sure that the extra element (in ENML case, en-media) is defined as a block-level element. To do that you create your own NamespaceHandler and you make sure that you return "display: block;" for en-media in the implementation of NamespaceHandler.getNonCssStyling. (See ENMLNamespaceHandler.java below)

        ....
        XHTMLPanel panel = new XHTMLPanel();
        panel.setDocument(doc,"",new ENMLNamespaceHandler(new XhtmlNamespaceHandler()));
        ....
        class ENMLNamespaceHandler implements NamespaceHandler {
        ....
           public String getNonCssStyling(Element e) {
               String toReturn = delegate.getNonCssStyling(e);
               if ("en-media".equalsIgnoreCase(e.getNodeName())) {
                  toReturn = "display: block;";
               }
               return toReturn;
           }
        ....
        }

With that you ensure that xhtmlrenderer will call ReplacedElementFactory.createReplacedElement for en-media. Now you must supply a ReplacedElementFactory that it’s able to process en-media. Usually the implementation of the createReplacedElement() involves creating a Swing JComponent, wrapping it in a SwingReplacedElement and adding it to the LayoutContext.getCanvas().

        ....
       ReplacedElementFactory cef = new ENMLReplacedElementFactory(new SwingReplacedElementFactory());
       XHTMLPanel panel = new XHTMLPanel();
       panel.getSharedContext().setReplacedElementFactory(cef);
       panel.setDocument(doc,"",new ENMLNamespaceHandler(new XhtmlNamespaceHandler()));
        ....
 
       public class ENMLReplacedElementFactory implements ReplacedElementFactory {
       ...
          public ReplacedElement createReplacedElement(LayoutContext context, BlockBox box,
                                                                                      UserAgentCallback uac, int cssWidth, int cssHeight) 
          {
 
             if ("en-media".equals(box.getElement().getNodeName())) {
                    JTextArea cc = new JTextArea();
                   cc.setText("Missing implementation for en-media");
                   cc.setSize(cc.getPreferredSize());
 
                   context.getCanvas().add(cc);
 
                   ReplacedElement toReturn = new SwingReplacedElement(cc) {
                     public boolean isRequiresInteractivePaint() {
                         return false;
                     }
                  };
 
                 return toReturn;
         }
 
        ReplacedElement toReturn = delegate.createReplacedElement(context, box, uac, cssWidth, cssHeight);
        return toReturn;
    }
....
}

References:

  1. Experiment: embedding Flash in Flying Saucer
  2. NamespaceHandler
  3. ReplacedElementFactory
  4. Re: custom layout support
  5. http://wiki.java.net/bin/view/Javadesktop/TheFlyingSaucerInProcessFAQ

ENMLNamespaceHandler.java:

package com.rubenlaguna.en4j.NoteContentViewModule;
 
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xhtmlrenderer.css.extend.StylesheetFactory;
import org.xhtmlrenderer.css.sheet.StylesheetInfo;
import org.xhtmlrenderer.extend.NamespaceHandler;
 
/**
 *
 * @author ecerulm
 */
class ENMLNamespaceHandler implements NamespaceHandler {
    private final NamespaceHandler delegate;
 
    public ENMLNamespaceHandler(NamespaceHandler h) {
        this.delegate = h;
    }
 
    public boolean isImageElement(Element e) {
        return delegate.isImageElement(e);
    }
 
    public boolean isFormElement(Element e) {
        return delegate.isFormElement(e);
    }
 
    public StylesheetInfo[] getStylesheets(Document doc) {
        return delegate.getStylesheets(doc);
    }
 
    public String getNonCssStyling(Element e) {
        String toReturn = delegate.getNonCssStyling(e);
        if ("en-media".equalsIgnoreCase(e.getNodeName())) {
            toReturn = "display: block;";
        }
        Logger.getLogger(ENMLNamespaceHandler.class.getName()).info("style for ("+e.getNodeName()+") is ("+toReturn+")");
        return toReturn;
    }
 
    public String getNamespace() {
        return delegate.getNamespace();
    }
 
    public String getLinkUri(Element e) {
        return delegate.getLinkUri(e);
    }
 
    public String getLang(Element e) {
        return delegate.getLang(e);
    }
 
    public String getImageSourceURI(Element e) {
        return delegate.getImageSourceURI(e);
    }
 
    public String getID(Element e) {
        return delegate.getID(e);
    }
 
    public String getElementStyling(Element e) {
        return delegate.getElementStyling(e);
    }
 
    public String getDocumentTitle(Document doc) {
        return delegate.getDocumentTitle(doc);
    }
 
    public StylesheetInfo getDefaultStylesheet(StylesheetFactory factory) {
        return delegate.getDefaultStylesheet(factory);
    }
 
    public String getClass(Element e) {
        return delegate.getClass(e);
    }
 
    public String getAttributeValue(Element e, String namespaceURI, String attrName) {
        return delegate.getAttributeValue(e, namespaceURI, attrName);
    }
 
    public String getAttributeValue(Element e, String attrName) {
        return delegate.getAttributeValue(e, attrName);
    }
 
    public String getAnchorName(Element e) {
        return delegate.getAnchorName(e);
    }
 
}

ENMLReplacedElementFactory.java:

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rubenlaguna.en4j.NoteContentViewModule;
 
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import org.w3c.dom.Element;
import org.xhtmlrenderer.extend.ReplacedElement;
import org.xhtmlrenderer.extend.ReplacedElementFactory;
import org.xhtmlrenderer.extend.UserAgentCallback;
import org.xhtmlrenderer.layout.LayoutContext;
import org.xhtmlrenderer.render.BlockBox;
import org.xhtmlrenderer.simple.extend.FormSubmissionListener;
import org.xhtmlrenderer.swing.EmptyReplacedElement;
import org.xhtmlrenderer.swing.ImageReplacedElement;
import org.xhtmlrenderer.swing.SwingReplacedElement;
import org.xhtmlrenderer.swing.SwingReplacedElementFactory;
import org.xhtmlrenderer.util.ImageUtil;
 
/**
 *
 * @author ecerulm
 */
class ENMLReplacedElementFactory implements ReplacedElementFactory {
 
    private final SwingReplacedElementFactory delegate;
    private final Logger LOG = Logger.getLogger(ENMLReplacedElementFactory.class.getName());
 
    public ENMLReplacedElementFactory(SwingReplacedElementFactory delegate) {
        this.delegate = delegate;
    }
 
    public void setFormSubmissionListener(FormSubmissionListener fsl) {
        delegate.setFormSubmissionListener(fsl);
    }
 
    public void reset() {
        delegate.reset();
    }
 
    public void remove(Element e) {
        delegate.remove(e);
    }
 
    public ReplacedElement createReplacedElement(LayoutContext context, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {
        ReplacedElement toReturn = null;
 
 
        Logger.getLogger(ENMLReplacedElementFactory.class.getName()).log(Level.INFO, "Element:" + box.getElement().getNodeName());
        Logger.getLogger(ENMLReplacedElementFactory.class.getName()).log(Level.INFO, "Element content:" + box.getElement().getNodeValue());
 
        if ("en-media".equals(box.getElement().getNodeName())) {
            //if("image/jpeg".equalsIgnoreCase(box.getElement().getAttribute("type"))){
            //
            //    toReturn = loadImage()
            //            return toReturn;
            //}
            toReturn = brokenImage(context, 100, 100);
        }
 
        if (null == toReturn) {
            toReturn = delegate.createReplacedElement(context, box, uac, cssWidth, cssHeight);
        }
 
        return toReturn;
    }
 
    private ReplacedElement brokenImage(LayoutContext context, int cssWidth, int cssHeight) {
 
        //TODO: add a real implementation that returns an image
        ReplacedElement toReturn = null;
 
        JTextArea cc = new JTextArea();
        cc.setText("Missing implementation for en-media");
        //cc.setPreferredSize(new Dimension(cssWidth, cssHeight));
        cc.setSize(cc.getPreferredSize());
 
        context.getCanvas().add(cc);
 
        toReturn = new SwingReplacedElement(cc) {
 
            public boolean isRequiresInteractivePaint() {
                return false;
            }
        };
 
        return toReturn;
    }
}

Incorrect track length in iTunes

iTunes was reporting a incorrect track time length for an audiobook that I created concatenating several mp3 together.

I had to reencode the whole thing again to make iTunes recognize propertly the track lenght. I could’t reencode the concatenated file itself as that ended up with a Segmentation fault in lame

cat *.mp3 |lame --mp3input - - >resultfile.mp3
 
cat *Night.mp3 |lame --mp3input -  down.mp3
ID3v2 found. Be aware that the ID3 tag is currently lost when transcoding.
LAME 3.98.2 64bits (http://www.mp3dev.org/)
Using polyphase lowpass filter, transition band: 16538 Hz - 17071 Hz
Encoding <stdin> to down.mp3
Encoding as 44.1 kHz single-ch MPEG-1 Layer III (11x)  64 kbps qval=3
bitstream problem: resyncing...
bitstream problem: resyncing...
bitstream problem: resyncing...
bitstream problem: resyncing...

This uses the lame streaming mode, decodes the input stream (all mp3 concatenated) as mp3 and then reencodes it as mp3. Although it complains about “bitstream problem: resyncing...“, the resulting mp3 sounds fine.