Wednesday, October 12, 2011

How to Exclude Scala/Java Source Files by Name in sbt

I thought this small tip would be useful to someone as I recently needed to do this and didn't know how to.

The project base directory is by default a source directory in addition to src/main/scala. You can exclude source files by name (butler.scala in the example below) like:

excludeFilter in unmanagedSources := "butler.scala"

Read more on How to exclude .scala source file in project folder - sbt Google Groups, also checkout Classpaths - sbt Wiki.

To learn more about Scala programming, I recommend Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.

Tuesday, June 21, 2011

Using UnboundID LDAP SDK Directory API from Scala

I'm very new in the world of LDAP Directory Services. After installing ApacheDS Server, Apache Directory Studio LDAP GUI Client, and trying the plain old ldapsearch and ldapcompare command-line utilities, it was time for me to do some integration with Java applications (I'm actually trying to query LDAP directory from Ant build script task).

The most popular and programmer-friendly LDAP API at the moment is UnboundID LDAP SDK. To get a feel of how it works, I decided to use Scala REPL interpreter. I really love this tool (and the Scala programming language itself) more and more every time I use it!

The screen captures below doesn't do justice to the Scala interpreter. The tab-completion works really well!
Indeed the UnboundID LDAP SDK is really easy to use, and it's better with powerful Scala & its versatile interpreter for a quick ride. :-)

Learn Scala programming language quicker! Get Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition.

Connect & Bind using UnboundID LDAP SDK and Scala

ceefour@annafi:~/vendor/unboundid-ldapsdk-2.2.0-se$ scala -cp unboundid-ldapsdk-se.jar  Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_25). Type in expressions to have them evaluated. Type :help for more information. scala> import com.unboundid.ldap.sdk._ import com.unboundid.ldap.sdk._ scala> val con = new LDAPConnection con: com.unboundid.ldap.sdk.LDAPConnection = LDAPConnection(not connected) scala> con.connect("localhost", 10389) scala> con.bind("uid=admin,ou=system","password") res5: com.unboundid.ldap.sdk.BindResult = LDAPResult(resultCode=0 (success), messageID=1)

Connect & Bind via SSL

scala> import com.unboundid.util.ssl._ import com.unboundid.util.ssl._ scala> var sslUtil = new SSLUtil( new TrustAllTrustManager() ) sslUtil: com.unboundid.util.ssl.SSLUtil = com.unboundid.util.ssl.SSLUtil@43763e0b scala> var socketFactory = sslUtil.createSSL createSSLContext createSSLServerSocketFactory createSSLSocketFactory scala> var socketFactory = sslUtil.createSSLSocketFactory socketFactory: javax.net.ssl.SSLSocketFactory = com.sun.net.ssl.internal.ssl.SSLSocketFactoryImpl@43d7a5a scala> val con = new LDAPConnection LDAPConnection LDAPConnectionInternals LDAPConnectionOptions LDAPConnectionPool LDAPConnectionPoolHealthCheck LDAPConnectionPoolHealthCheckThread LDAPConnectionPoolStatistics LDAPConnectionReader LDAPConnectionStatistics scala> val con = new LDAPConnection(socketFactory, "localhost", 10636) con: com.unboundid.ldap.sdk.LDAPConnection = LDAPConnection(connected to localhost:10636) scala> con.bind("uid=admin,ou=system","password") res18: com.unboundid.ldap.sdk.BindResult = LDAPResult(resultCode=0 (success), messageID=1)

Search

scala> val results=con.search("ou=system",SearchScope.SUB,"(uid=admin)") results: com.unboundid.ldap.sdk.SearchResult = SearchResult(resultCode=0 (success), messageID=3, entriesReturned=1, referencesReturned=0) scala> results.getSearchEntries res8: java.util.List[com.unboundid.ldap.sdk.SearchResultEntry] = [SearchResultEntry(dn='uid=admin,ou=system', messageID=3, attributes={Attribute(name=uid, values={'admin'}), Attribute(name=keyAlgorithm, values={'RSA'}), Attribute(name=sn, values={'administrator'}), Attribute(name=objectClass, values={'person', 'organizationalPerson', 'inetOrgPerson', 'tlsKeyInfo', 'top'}), Attribute(name=displayName, values={'Directory Superuser'}), Attribute(name=cn, values={'system administrator'}), Attribute(name=publicKey, base64Values={'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJjX2EbUDyBZYFPfsSNMZRsW3mLc/KiKRO6pck3J3eFMKErXA7jxpiho/Eoc6RWgxut2K3aITnQZIl1hHF6Hv30CAwEAAQ=='}), Attribute(name=privateKeyFormat, values={'PKCS#8'}), Attribute(name=publicKeyFormat, values={'X.509'}), Attribute(name=privateKey, ba... scala> results.getSearchEntries.get(0) res11: com.unboundid.ldap.sdk.SearchResultEntry = SearchResultEntry(dn='uid=admin,ou=system', messageID=3, attributes={Attribute(name=uid, values={'admin'}), Attribute(name=keyAlgorithm, values={'RSA'}), Attribute(name=sn, values={'administrator'}), Attribute(name=objectClass, values={'person', 'organizationalPerson', 'inetOrgPerson', 'tlsKeyInfo', 'top'}), Attribute(name=displayName, values={'Directory Superuser'}), Attribute(name=cn, values={'system administrator'}), Attribute(name=publicKey, base64Values={'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJjX2EbUDyBZYFPfsSNMZRsW3mLc/KiKRO6pck3J3eFMKErXA7jxpiho/Eoc6RWgxut2K3aITnQZIl1hHF6Hv30CAwEAAQ=='}), Attribute(name=privateKeyFormat, values={'PKCS#8'}), Attribute(name=publicKeyFormat, values={'X.509'}), Attribute(name=privateKey, base64Values={'MII...

Compare

scala> con.compare("uid=admin,ou=system", "userPassword", "password") res12: com.unboundid.ldap.sdk.CompareResult = LDAPResult(resultCode=5 (compare false), messageID=4, opType='compare', matchedDN='uid=admin,ou=system') scala> con.compare("uid=admin,ou=system", "uid", "admin") res13: com.unboundid.ldap.sdk.CompareResult = LDAPResult(resultCode=6 (compare true), messageID=5, opType='compare', matchedDN='uid=admin,ou=system')

Get Entry & Attribute Value

scala> val entry = con.getEntry("uid=admin,ou=system") entry: com.unboundid.ldap.sdk.SearchResultEntry = SearchResultEntry(dn='uid=admin,ou=system', messageID=7, attributes={Attribute(name=uid, values={'admin'}), Attribute(name=keyAlgorithm, values={'RSA'}), Attribute(name=sn, values={'administrator'}), Attribute(name=objectClass, values={'person', 'organizationalPerson', 'inetOrgPerson', 'tlsKeyInfo', 'top'}), Attribute(name=displayName, values={'Directory Superuser'}), Attribute(name=cn, values={'system administrator'}), Attribute(name=publicKey, base64Values={'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJjX2EbUDyBZYFPfsSNMZRsW3mLc/KiKRO6pck3J3eFMKErXA7jxpiho/Eoc6RWgxut2K3aITnQZIl1hHF6Hv30CAwEAAQ=='}), Attribute(name=privateKeyFormat, values={'PKCS#8'}), Attribute(name=publicKeyFormat, values={'X.509'}), Attribute(name=privateKey, base64Values={'MII... scala> entry.getAttribute getAttribute getAttributeValue getAttributeValueAsBoolean getAttributeValueAsDN getAttributeValueAsDate getAttributeValueAsInteger getAttributeValueAsLong getAttributeValueByteArrays getAttributeValueBytes getAttributeValues getAttributes getAttributesWithOptions scala> entry.getAttribute def getAttribute(String): Attribute def getAttribute(String, schema.Schema): Attribute scala> entry.getAttribute("userPassword") res16: com.unboundid.ldap.sdk.Attribute = Attribute(name=userPassword, values={'{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g='}) scala> entry.getAttributeValue("userPassword") res17: java.lang.String = {SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=

Scala 2.9.0.1 final version released: programming language with parallel features for Java VM

UPDATE: Scala 2.9.0.1 has replaced 2.9.0 as it hot-fixed several important bugs.

Scala 2.9.0 final:

We are happy to announce the release of the new stable release of the Scala distribution. The new Scala 2.9.0 final is available from our Download Page. The Scala 2.9.0 codebase includes several additions, notably the new Parallel Collections, but it also introduces improvements on many existing features, and contains many bug fixes.

Scala 2.9.0 binaries are available for the following libraries:

Of course.

Here are some recommended learning resources on Scala programming language:

Also, the Typesafe Stack is also out, which brings together Scala, Akka, and a few other things to get one up-and-running quickly. Much fun.

On the collection side of things, one of the first questions I saw was: do parallel collections share a common interface with standard collections. The answer is yes, they do, but not one that existed in 2.8.1.

You see, a trouble with parallel collections is that, now that they are available, people will probably be passing them around. If they could be passed to old code -- as it was briefly contemplated -- that old code could crash in mysterious ways. In fact, it happens with REPL itself.

For that reason, ALL of your code comes with a guarantee that it will only accept sequential collections. In other words, Iterable, Seq, Set, etc, they all now share a guarantee to be sequential, which means you cannot pass a parallel sequence to a method expecting Seq.

The parallel collections start with Par: ParIterable, ParSeq, ParSet and ParMap. No ParTraversable for now. These are guaranteed to be parallel. They can be found inside scala.collection.parallel, scala.collection.parallel.immutable, etc.

You can also get a parallel collection just by calling the ".par" method on it, and, similarly, the ".seq" method will return a sequential collection.

Now, if you want your code to not care whether it receives a parallel or sequential collection, you should prefix it with Gen: GenTraversable, GenIterable, GenSeq, etc. These can be either parallel or sequential.

And, now, something fun to try out:

def p[T](coll: collection.GenIterable[T]) = coll foreach println; p(1 to 20); p((1 to 20).par)

 

I highly recommend the following books for more information about Scala programming language:

 

 

Copied mostly verbatim from this announcement and that announcement.

Sunday, December 26, 2010

How to Dump/Inspect Object or Variable in Java

Scala (console) has a very useful feature to inspect or dump variables / object values :

scala> def b = Map("name" -> "Yudha", "age" -> 27)
b: scala.collection.immutable.Map[java.lang.String,Any]

scala> b
res1: scala.collection.immutable.Map[java.lang.String,Any] = Map((name,Yudha), (age,27))

Inside our application, especially in Java programming language (although the techniques below obviously works with any JVM language like Scala and Groovy) sometimes we want to inspect/dump the content of an object/value. Probably for debugging or logging purposes.

My two favorite techniques is just to serialize the Java object to JSON and/or XML. An added benefit is that it's possible to deserialize the dumped object representation back to an actual object if you want.

JSON Serialization with Jackson

Depend on Jackson (using Maven):
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.6.3</version>
</dependency>
Then use it:
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

..
Logger logger = LoggerFactory.getLogger(getClass());

@Test
public void level() throws ServiceException, JsonGenerationException, JsonMappingException, IOException {
MagentoServiceLocator locator = new MagentoServiceLocator();
Mage_Api_Model_Server_HandlerPortType port = locator.getMage_Api_Model_Server_HandlerPort();
String sessionId = port.login("...", "...");
logger.info(String.format("Session ID = %s", sessionId));
Map[] categories = (Map[]) port.call(sessionId, "catalog_category.level", new Object[] { null, null, 2 } );
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
logger.info( mapper.writeValueAsString(categories) );
}

Example output :

6883 [main] INFO id.co.bippo.shop.magentoclient.AppTest - [ {
  "position" : "1",
  "level" : "2",
  "is_active" : "1",
  "name" : "Gamis",
  "category_id" : "3",
  "parent_id" : 2
}, {
  "position" : "2",
  "level" : "2",
  "is_active" : "1",
  "name" : "Celana",
  "category_id" : "5",
  "parent_id" : 2
} ]

XML Serialization with XStream

As a pre-note, XStream can also handle JSON with either Jettison or its own JSON driver, however people usually prefer Jackson than XStream for JSON serialization.

Maven dependency for XStream:
<dependency>
<groupId>xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2.2</version>
</dependency>
Use it:
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.Map;

import javax.xml.rpc.ServiceException;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.thoughtworks.xstream.XStream;
...
@Test
public void infoXml() throws ServiceException, RemoteException {
MagentoServiceLocator locator = new MagentoServiceLocator();
Mage_Api_Model_Server_HandlerPortType port = locator.getMage_Api_Model_Server_HandlerPort();
String sessionId = port.login("...", "...");
logger.info(String.format("Session ID = %s", sessionId));
Map category = (Map) port.call(sessionId, "catalog_category.info",
new Object[] { 3 } );
XStream xstream = new XStream();
logger.info( xstream.toXML(category) );
}

Sample output:

5949 [main] INFO id.co.bippo.shop.magentoclient.AppTest - <map>
  <entry>
    <string>position</string>
    <string>1</string>
  </entry>
  <entry>
    <string>custom_design</string>
    <string></string>
  </entry>
  <entry>
    <string>custom_use_parent_settings</string>
    <string>0</string>
  </entry>
  <entry>
    <string>custom_layout_update</string>
    <string></string>
  </entry>
  <entry>
    <string>include_in_menu</string>
    <string>1</string>
  </entry>
  <entry>
    <string>custom_apply_to_products</string>
    <string>0</string>
  </entry>
  <entry>
    <string>meta_keywords</string>
    <string>gamis, busana muslim</string>
  </entry>
  <entry>
    <string>available_sort_by</string>
    <string></string>
  </entry>
  <entry>
    <string>url_path</string>
    <string>gamis.html</string>
  </entry>
  <entry>
    <string>children</string>
    <string></string>
  </entry>
  <entry>
    <string>landing_page</string>
    <null/>
  </entry>
  <entry>
    <string>display_mode</string>
    <string>PRODUCTS</string>
  </entry>
  <entry>
    <string>level</string>
    <string>2</string>
  </entry>
  <entry>
    <string>description</string>
    <string>Gamis untuk muslimah</string>
  </entry>
  <entry>
    <string>name</string>
    <string>Gamis</string>
  </entry>
  <entry>
    <string>path</string>
    <string>1/2/3</string>
  </entry>
  <entry>
    <string>created_at</string>
    <string>2010-12-24 11:37:41</string>
  </entry>
  <entry>
    <string>children_count</string>
    <string>0</string>
  </entry>
  <entry>
    <string>is_anchor</string>
    <string>1</string>
  </entry>
  <entry>
    <string>url_key</string>
    <string>gamis</string>
  </entry>
  <entry>
    <string>parent_id</string>
    <int>2</int>
  </entry>
  <entry>
    <string>filter_price_range</string>
    <null/>
  </entry>
  <entry>
    <string>all_children</string>
    <string>3</string>
  </entry>
  <entry>
    <string>is_active</string>
    <string>1</string>
  </entry>
  <entry>
    <string>page_layout</string>
    <string></string>
  </entry>
  <entry>
    <string>image</string>
    <null/>
  </entry>
  <entry>
    <string>category_id</string>
    <string>3</string>
  </entry>
  <entry>
    <string>default_sort_by</string>
    <null/>
  </entry>
  <entry>
    <string>custom_design_from</string>
    <null/>
  </entry>
  <entry>
    <string>updated_at</string>
    <string>2010-12-24 11:37:41</string>
  </entry>
  <entry>
    <string>meta_description</string>
    <string>Jual baju gamis untuk muslim</string>
  </entry>
  <entry>
    <string>custom_design_to</string>
    <null/>
  </entry>
  <entry>
    <string>path_in_store</string>
    <null/>
  </entry>
  <entry>
    <string>meta_title</string>
    <string>Gamis</string>
  </entry>
  <entry>
    <string>increment_id</string>
    <null/>
  </entry>
</map>

Which one is better?

I personally prefer JSON, but fortunately, you always have a choice. :-)

Monday, December 20, 2010

Eclipse RAP Single Sourcing Awesomeness (with EMF Editor and Teneo+Hibernate as bonus!)

Eclipse Rich Client Platform has come a looong way since it was first introduced (and used in Eclipse IDE). The new Eclipse RAP (Rich Application Platform) is also becoming more and more attractive for deploying existing or new Eclipse RCP applications to the web.

One of my the projects I'm working on is developed on top of Eclipse RCP. It uses additional plugins such as EMF (Eclipse Modeling Framework) including EMF Editor UI, Teneo (EMF Persistence for Relational Databases), and Hibernate.

After some work, I managed to run the whole application on both Eclipse RCP (desktop) and Eclipse RAP (web-based). See the screenshots for proof.

Thanks to the recently released EMF Support for RAP I don't have to let go any of the nice EMF generated editor UIs for the web-based RAP version.

What's amazing is how little the work I have to do to port the RCP app to RAP.

The changes I needed to do is not changing code, but juggling dependencies to plugins and/or packages. Also creating a few platform-specific plugins (different based on whether I deploy on RCP or RAP).

It boils down to:

  1. Do not hard-depend on org.eclipse.ui plugin. Either depend on both org.eclipse.ui and org.eclipse.rap.ui plugins as optional dependencies, or import the specific packages. I prefer optional dependency on both plugins because it's much faster and easier.
  2. Be aware that there will be multiple sessions at once.
  3. 2D Drawing functions are not yet fully available. (and I guess will never be available)

See the Eclipse RAP FAQ on Single Sourcing for more information.

Fixing error: The type org.eclipse.core.runtime.IAdaptable cannot be resolved. It is indirectly referenced from required .class files.

If you get one of the following errors :

The type org.eclipse.core.runtime.IAdaptable cannot be resolved. It is indirectly referenced from required .class files.

The type org.eclipse.core.runtime.CoreException cannot be resolved. It is indirectly referenced from required .class files.

First of all, check that your plugins depend on org.eclipse.core.runtime plugin.

The classes above are located in org.eclipse.equinox.common plugin, and should be included (along with org.eclipse.core.runtime plugin) in your target platform.

If it still occurs, most likely you get your target platform plugins mixed up. I got this error because I tried to mix plugins from my Eclipse IDE installation (Helios 3.6-SR-1) with Eclipse 3.7M4 Platform plugins.

Simply unchecking the plugins from target platform Contents tab is not enough. I have to actually remove the location. If you notice duplicate plugins in your Target Platform Definition's Contents tab, then you're getting this problem.

Remove the offending plugins location from the target platform definition. If you must add plugins from Eclipse IDE location, cherry pick each plugin from the Locations UI, instead of just adding the whole SDK and unchecking them from the Contents tab.

Sunday, December 19, 2010

Creating an About Dialog for your Eclipse RCP Application

Displaying an About box in your Eclipse RCP Application/Product is actually very simple. However as is typical of a framework ("don't call us, we'll call you" principle), there are conventions you must follow.

Before you do this, you must already create an Eclipse RCP Application class that implements IApplication and register it as an Eclipse extension in your plugin.xml.

Adding the Help > About Menu Item


First, edit your ApplicationActionBarAdvisor class (which extends org.eclipse.ui.application.ActionBarAdvisor base class), as follows:

    protected void makeActions(IWorkbenchWindow window) {
        aboutAction = ActionFactory.ABOUT.create(window);
        register(aboutAction);
    }

That will register the About action. You will also need to add the menu action itself to the menu bar:

    protected void fillMenuBar(IMenuManager menuBar) {
        MenuManager helpMenu = new MenuManager("&Help", IWorkbenchActionConstants.M_HELP);
       
        menuBar.add(helpMenu);
        helpMenu.add(aboutAction);
    }

Launch your Eclipse RCP application and you can display the About dialog.

Customizing the About Dialog Box


To customize the About dialog box's contents, first you must create an Eclipse RCP Product Configuration.
Click File > New > Product Configuration and define your product.

This should also add your product to extension point 'org.eclipse.core.runtime.products'.

Edit your project's Launch Configuration to use the your Product Configuration instead of Eclipse Application. Verify that it works well.

Now you can customize the About box contents by editing your Product Configuration (.product file), go to Branding tab and About Dialog section. Specify the About Image and About Text there.

To specify the image, import an image resource (PNG, GIF, JPG) to your plugin project (e.g. inside an /icons folder). Important: Your image will need to be included inside your resulting plugin binary. Edit your plugin's manifest, go to Build tab, and make sure your images/icons are included (checked) for the binary build and source build.

After editing your Product Configuration, you must synchronize it with the plugin manifest. Go to the Product's Overview tab and click Synchronize (under Testing).

That action will update several properties in the org.eclipse.core.runtime.products extension, for example:

   <extension
         id="abispulsa_rcp"
         point="org.eclipse.core.runtime.products">
      <product
            application="com.abispulsa.bisnis.rcp.application"
            description="Layanan bagi korporasi untuk dapat dengan mudah mengisi pulsa."
            name="AbisPulsa Bisnis RCP">
         <property
               name="appName"
               value="AbisPulsa Bisnis RCP">
         </property>
         <property
               name="aboutText"
               value="AbisPulsa Bisnis merupakan layanan bagi korporasi untuk dapat dengan mudah mengisi pulsa.">
         </property>
         <property
               name="aboutImage"
               value="icons/AbisPulsa_icon_75x75.png">
         </property>
      </product>
   </extension>

For your information: Other properties you can use include:

  • windowImages
  • aboutImage
  • aboutText
  • appName
  • welcomePage
  • preferenceCustomization

References:
  1. http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/product_def_extpt.htm
  2. http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_core_runtime_products.html