Showing posts with label Java Tips and Tricks. Show all posts
Showing posts with label Java Tips and Tricks. Show all posts

Thursday, March 29, 2012

How to Deploy Maven Artifacts to WebDAV Repository

Either the DAV wagon is great or DreamHost's Svn+WebDAV integration is great, or both. POM Configuration is straightforward:

<distributionManagement>
<snapshotRepository>
<id>soluvas.org.snapshots.tmp</id>
<url>dav:http://maven.soluvas.org/snapshots</url>
</snapshotRepository>
</distributionManagement>

It "works" in Eclipse sometimes, but when the artifact has not yet been uploaded (huh?!?!) it fails with:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.5:deploy (default-deploy) on project soluvas-apacheds: Failed to deploy artifacts: Could not find artifact org.soluvas.apacheds:soluvas-apacheds:jar:1.0.0-20120329.222133-1 in soluvas.org.snapshots.tmp (dav:http://maven.soluvas.org/snapshots) -> [Help 1]

In mvn CLI, it simply fails: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.5:deploy (default-deploy) on project soluvas-apacheds: Failed to deploy artifacts/metadata: No connector available to access repository soluvas.org.snapshots.tmp (dav:http://maven.soluvas.org/snapshots) of type default using the available factories WagonRepositoryConnectorFactory -> [Help 1]

Anyway, to properly do this you need a wagon config, which works well with DreamHost's subversion+webDAV (haven't tried with plain Webdav repository yet, but should work):
<build>
<extensions>
<extension>
<artifactId>wagon-webdav-jackrabbit</artifactId>
<groupId>org.apache.maven.wagon</groupId>
<version>2.2</version>
</extension>
</extensions>
</build>

then you add your credentials in ~/.m2/settings.xml :

<settings xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">


<servers>

<server>

<id>soluvas.org.snapshots.tmp</id>

<username>youruser</username>

<password>yourpass</password>

</server>

</servers>

...

</settings>

Now you can deploy your Maven project by:

mvn source:jar javadoc:jar install deploy


To learn more about Java Web Development using Java EE 6, I highly recommend The Java EE 6 Tutorial: Basic Concepts (4th Edition) (Java Series) by Eric Jendrock, Ian Evans, Devika Gollapudi and Kim Haase.

Wednesday, December 15, 2010

Gradle Build Script (build.gradle) for Writing Gradle Plugins

Gradle is a great build system written in Groovy programming language and that uses build scripts written in Groovy as well.

There are several ways to write a Gradle plugin and the most extensible one (also the most complicated) is to build the plugin as a separate project.

I tried to find documentation on how to do this but I can only find actual guide for writing "embedded" Gradle plugins (the plugins are contained inside buildSrc folder). But to move the plugin out of buildSrc folder and make it independent, you can use a build.gradle build script file like this in your plugin:


apply {
    plugin 'java'
    plugin 'groovy'
    plugin 'maven'
    plugin 'eclipse'
}

group = 'gradle-plugin-javafx'
version = '0.2.0-SNAPSHOT'

dependencies {
    compile gradleApi()
    groovy localGroovy()
}

Change group and version properties with your own.

Also, you can leave out applying the 'java', 'eclipse', and 'maven' plugins if you don't use them.

Source: http://code.google.com/p/gradle-plugin-javafx/source/browse/build.gradle

Saturday, March 20, 2010

Parse Java Source Code with JaxMe JS

You can parse Java Source Code easily with JaxMe JS.

Here is a code examples that demonstrates how to use JaxME JS. The code won't work as it is, but it's useful to demonstrate JaxMe JS usage in practice.

A weakness of JaxMe JS is that it can only parse and generate Java 1.4 source code files. As of version 2, JaxMe JS cannot parse/generate Java 5 source code that uses Java generics and Java annotations features.

        JavaSourceFactory jsf = new JavaSourceFactory();
        JavaParser jp = new JavaParser(jsf);
        jp.parse(file);
        Iterator<JavaSource> javaSources = jsf.getJavaSources();
        JavaSource javaSource = javaSources.next();
        ObjectModel model = new ObjectModel(javaSource.getPackageName(), javaSource.getClassName());
        try {
            for (JavaField field : javaSource.getFields()) {
                log.log(Level.INFO, "Processing " + field.asString());
                model.addProperty(field.getName(), Class.forName(field
                        .getType().toString()));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new ParserException(e);
        }
        return model; 
Put the following on your Maven project's pom.xml to specify the JaxME JS depency. ANTLR must be specified too.
    <dependencies>
        <dependency>
            <groupId>org.apache.ws.jaxme</groupId>
            <artifactId>jaxmejs</artifactId>
            <version>0.5.2</version>
        </dependency>
        <dependency>
         <groupId>antlr</groupId>
         <artifactId>antlr</artifactId>
         <version>2.7.7</version>
        </dependency>
    </dependencies>
For more information on grammar/language parsing and code generation with Java (and ANTLR), get the book The Definitive Antlr Reference: Building Domain-Specific Languages (Pragmatic Programmers).

Using AspectJ for AOP on Maven project

AspectJ is used for Aspect Oriented Programming (AOP) in Java.

To configure AspectJ inside a Maven project you use aspectj-maven-plugin.

Modify your Maven project's pom.xml as follows:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
...
    <properties>
        <aspectj.version>1.6.8</aspectj.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.3</version>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>       <!-- use this goal to weave all your main classes -->
                            <goal>test-compile</goal>  <!-- use this goal to weave all your test classes -->
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

When you want to upgrade AspectJ version, simply change aspectj.version from properties.

Specify Java Source Level using Maven

To develop Java applications using Java 5 or Java 6 with Maven, add this to the project's pom.xml :

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

Replace "1.5" if necessary. Valid values are: 1.3, 1.4, 1.5, 1.6.

Thursday, March 11, 2010

Solving Maven Deploy/Release Problem: Unsupported Protocol: 'dav': Cannot find wagon which supports the requested protocol: dav

Are you getting the following error while using Maven and Release/Deploy Plugin on a WebDAV repository?
[INFO] [INFO] [deploy:deploy {execution: default-deploy}]
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [ERROR] BUILD ERROR
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] Error deploying artifact: Unsupported Protocol: 'dav': Cannot find wagon which supports the requested protocol: dav
[INFO]
[INFO] Component descriptor cannot be found in the component repository: org.apache.maven.wagon.Wagondav.
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] For more information, run Maven with the -e switch
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] [INFO] Total time: 7 seconds
[INFO] [INFO] Finished at: Fri Mar 12 01:01:37 WIT 2010
[INFO] [INFO] Final Memory: 20M/74M
[INFO] [INFO] ------------------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Maven execution failed, exit code: '1'

[INFO] ------------------------------------------------------------------------
[INFO] Trace
org.apache.maven.lifecycle.LifecycleExecutionException: Maven execution failed, exit code: '1'
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:719)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeStandaloneGoal(DefaultLifecycleExecutor.java:569)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:539)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:284)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
    at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
Caused by: org.apache.maven.plugin.MojoExecutionException: Maven execution failed, exit code: '1'
    at org.apache.maven.plugins.release.PerformReleaseMojo.execute(PerformReleaseMojo.java:133)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
    ... 17 more
Caused by: org.apache.maven.shared.release.ReleaseExecutionException: Maven execution failed, exit code: '1'
    at org.apache.maven.shared.release.phase.AbstractRunGoalsPhase.execute(AbstractRunGoalsPhase.java:89)
    at org.apache.maven.shared.release.phase.RunPerformGoalsPhase.execute(RunPerformGoalsPhase.java:67)
    at org.apache.maven.shared.release.DefaultReleaseManager.perform(DefaultReleaseManager.java:334)
    at org.apache.maven.shared.release.DefaultReleaseManager.perform(DefaultReleaseManager.java:282)
    at org.apache.maven.shared.release.DefaultReleaseManager.perform(DefaultReleaseManager.java:262)
    at org.apache.maven.plugins.release.PerformReleaseMojo.execute(PerformReleaseMojo.java:129)
    ... 19 more
Caused by: org.apache.maven.shared.release.exec.MavenExecutorException: Maven execution failed, exit code: '1'
    at org.apache.maven.shared.release.exec.InvokerMavenExecutor.executeGoals(InvokerMavenExecutor.java:375)
    at org.apache.maven.shared.release.exec.InvokerMavenExecutor.executeGoals(InvokerMavenExecutor.java:393)
    at org.apache.maven.shared.release.phase.AbstractRunGoalsPhase.execute(AbstractRunGoalsPhase.java:81)
    ... 24 more

Cause


If you have an old version of Maven, this is a known problem as the DAV wagon was not built in. Upgrade to the latest Maven version first (Maven 2.2.1 and later).

If you have a newer version of Maven, you might be using Debian or Ubuntu, which contains an incomplete version of Maven, that doesn't include the DAV wagon (wagon-webdav-jackrabbit).

Solution


Add this to your POM :
    <build>
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-webdav-jackrabbit</artifactId>
                <version>1.0-beta-6</version>
            </extension>
        </extensions>
    </build>
Tip: For Maven Release Plugin, you might want to use version 2.0.
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-release-plugin</artifactId>
                <version>2.0</version>
            </plugin>
        </plugins>
    </build>
In multi-module project, theoretically you just need to put this in the super POM. But I haven't checked it, it may not work on child modules. If so, add the "extension" part on all POMs that require deploying to a WebDAV repository server.

For in-depth information about Maven, get Apache Maven 2 Effective Implementation book.

Saturday, February 20, 2010

Subclipse Commit Hangs, Cannot Cancel, Blocks Save

Consider this scenario: You're committing a changeset to your Subversion repository with Subclipse in Eclipse IDE.

The Subversion commit takes a long time, probably due to flaky network connection (which happens often). In the meantime, you continue working and make changes. Somehow, the commit never finished, so you cancel. Alas, Subclipse cannot immediately cancel, it can only give you "Cancel Requested".

Subclipse apparently provides no clean way of stopping an SVN commit operation and restarting the commit in the case of network problems. Even worse, all your work in progress cannot be saved, since Subclipse is blocking it:



The window above has been showing in my screen for about 10 minutes now, and it will never go away anyway... until I quit Eclipse or (if I can't do that) kill Eclipse process by force.

I can't even save my files! It's very frustrating. Even if Subclipse cannot cancel SVN commit cleanly, it should allow me to at least save my files so I can restart Eclipse and try again!

Update: Submitted bug to Subclipse issue tracker

Wednesday, February 17, 2010

java.lang.NoClassDefFoundError: org/aspectj/lang/NoAspectBoundException

If you get this error:
java.lang.NoClassDefFoundError: org/aspectj/lang/NoAspectBoundException
The solution is:
Add the AspectJ library to the classpath.

If it's a web application, that means either:
  • Add the AspectJ Runtime (aspectjrt.jar) to WEB-INF/lib, or
  • In Eclipse, enable AspectJ on your project. Then open Project Properties, add the AspectJ Runtime library to Java EE Dependencies.

Monday, February 1, 2010

Logging to GUI by Writing Custom log4j Appender

All applications need logging. Logging means presenting useful information to the user. Logging can be configured separately from the code that emits logging information.

Apache log4j is a logging implementation library for Java. It can be used directly, however to make our application more portable, we usually use logging abstraction. I recommend Apache Commons Logging for this.

The most popular logging configurations are:
  • printing messages to standard console output
  • appending messages to a log file
However there is one popular use of logging that is seldom talked about, and that is logging to GUI.

For example, you have a text box and you want to log messages to it.

Of course you can do it the old fashioned way, by getting the text box object and appending messages directly to it. But other than it is not portable, and all of your code will be dependant on that text box object.

A more robust resolution would be to use log4j's flexibility. Here I will prove to you that flexibility does not have to equal complex.

log4j since version 1.2 adds a feature that is dynamic logging configuration at runtime. We'll use this feature to add a GUI Appender to log4j. An Appender's job is to take action of a log event. In our case, displaying the log message to a text box.

See the code below:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;

public class MainConsoleFrame extends javax.swing.JFrame {

    private Log log = LogFactory.getLog(MainFrame.class);
    private Appender guiAppender;
    ...

    public AuthoringConsoleFrame() {
        initComponents();
        
        // Setup logging to GUI
        final MainFrame frame = this;
        guiAppender = new AppenderSkeleton() {
  
     @Override
     public boolean requiresLayout() { return true; }
  
     @Override
     public void close() { }
  
     @Override
     protected void append(LoggingEvent event) {
             frame.consoleMessagesArea.append(layout.format(event));
         consoleMessagesArea.setCaretPosition(consoleMessagesArea.getText().trim().length() - 1);
            }
        };
 guiAppender.setLayout(new PatternLayout("%5p|%40.40c|%m%n"));
        Logger.getRootLogger().addAppender(guiAppender);
        
        // Log like this...
        log.info("Hello world!");
    }
    
    @Override
    protected void finalize() throws Throwable {
     Logger.getRootLogger().removeAppender(guiAppender);
     super.finalize();
    }

    ...
}    

Please note that I skipped a lot of boilerplate code in the above.

After setting up the Appender, you probably notice that the code for logging is now very simple. It uses Apache Commons Logging which means the application will work with any logging implementation. This makes code very portable, even if you remove the textbox or move code to a different application, logging code should just work.

I hope this post is helpful to you. Let me know if you have ideas or questions.

Friday, January 8, 2010

Unfuddle Mylyn Connector for Eclipse IDE

Unfuddle is an excellent source code hosting and online project management tool. It has a nicely done bug ticketing system, but I wanted something much cooler, a Mylyn Connector for Unfuddle.

Mylyn is an Eclipse plugin that makes it easy to create and edit tasks (or issues/tickets/bugs whatever you call it) right inside the Eclipse IDE. Mylyn also allows the developer to work locally (offline), and the changes can be synchronized with the remote task repository when online.

I work in Java primarily with SpringSource Tool Suite, which has Mylyn built-in. It made me sad that I seldom use Mylyn while working with Unfuddle-hosted projects... but that's old news.

Fortunately for me and all of you, Andronic Trandafir this morning brought good news that Unfuddle has developed the Unfuddle Mylyn Connector. They're running limited tests with some customers, and I'm lucky enough to have that privilege.

Unfuddle Plugin Installation on SpringSource Tool Suite


Installation couldn't be easier, just copy that plugin JARs into the Eclipse dropins folder, then restart Eclipse.

I was using SpringSource Tool Suite 2.3.0, the installation was flawless. Unfuddle plugin was immediately installed, no plugin conflicts whatsoever. This is quite a feat on Unfuddle developers' part because SpringSource Tool Suite is a very complex IDE.

Experience of Unfuddling in Mylyn


Setup Task Repository:



Synchronizing tickets from Unfuddle to Mylyn:


Editing a Ticket:


Want It?


At the time of this writing, Unfuddle Mylyn Connector is still in beta testing phase. Post your request in Unfuddle Community Forums to see if you can get a copy.

Unfuddle Mylyn Connector has been released. Check out their blog post announcing this plugin.

Thursday, January 7, 2010

Want Tail with GUI on Windows (and Linux and Mac)? MakeLogic Tail!

I usually do my Java development on Ubuntu (Linux) and tail is my best friend. Tail is a console tool to follow real-time updates on a file, for example the Apache Tomcat log file. However, in Windows, I don't have the "luxury" of tail

MakeLogic Tail, a GUI/visual Tail utility, comes to save my day! It is a Java application so it also runs on Linux and Mac OS X and other Java-supported platforms.



If you want the "real tail", alternatives are:

Wednesday, December 30, 2009

EventBus Event Notification Framework

EventBus is an Event Notification Framework.

Following example is from EventBus Getting Started guide.

Statusbar that updates based on published events, and no need to register statusbar control/widget as listener of publisher(s). Without EventBus, statusbar will need to be added as listener to many classes. Statusbar can also be created and destroyed at any time.

public StatusBar extends JLabel {
    public StatusBar() {
        AnnotationProcessor.process(this);
    }
    @EventSubscriber(eventClass=StatusEvent.class)
    public void updateStatus(StatusEvent statusEvent) {
        this.setText(statusEvent.getStatusText();
    }
}

A similar project is ELF (Event Listener Framework) but it seems to be less mature.

Taken from my StackOverflow answer about Generic annotation-driven event notification framework.

Publish-Subscribe Event Driven Programming

Publish-Subscribe messaging consists of loosely coupled system of topic/channel, publisher (provider), and subscriber (consumer).

Event Driven Programming allows the an objects to notify (i.e. publish an event) to observers.

Java programming model has long used the Observer/Listener pattern approach, and also Whiteboard pattern in OSGi environments.

However, there is no easy way and/or standardized convention to implement this.

Implementing the Observer pattern requires an object to extend Observable, which is "not POJO". Having listeners also clutter the class and there are a lot of boilerplate code to add:

private List<ClickListener> clickListeners = new ArrayList<ClickListener>();
private List<TouchListener> touchListeners = new ArrayList<TouchListener>();
public boolean addClickListener(ClickListener clickListener);
public boolean addTouchListener(TouchListener touchListener);
public List<ClickListener> getClickListeners();
public List<TouchListener> getTouchListeners();
public boolean removeClickListener(ClickListener clickListener);
public boolean removeTouchListener(TouchListener touchListener);
private void fireClick(ClickEvent clickEvent);
private void fireTouch(TouchEvent touchEvent);

See what I mean? That's only for two listeners, and those are only method signatures.

The implementation are probably trivial, but it's very boring to repeat these over and over again in most of your classes.

And what if the implementation isn't perfect? Will you change all those?

Over the next few posts, I'll explore the potential solutions.