Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

Sunday, June 23, 2013

Secure WebSockets with Jetty

Websockets is a protocol that runs on top of TCP and allows server to push data to the client, unlike with HTTP. Let's see how to use WebSockets with TLS using Eclipse Jetty.

Add following dependencies to the project POM

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.0.3.v20130506</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-server</artifactId>
    <version>9.0.3.v20130506</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-client</artifactId>
    <version>9.0.3.v20130506</version>
</dependency>

Create a websocket by annotating with @WebSocket

package org.amila.sample.websocket.server;

import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;

import java.io.IOException;

@WebSocket
public class MyWebSocket {
    private RemoteEndpoint remote;

    @OnWebSocketConnect
    public void onConnect(Session session) {
        System.out.println("WebSocket Opened");
        this.remote = session.getRemote();
    }

    @OnWebSocketMessage
    public void onMessage(String message) {
        System.out.println("Message from Client: " + message);
        try {
            remote.sendString("Hi Client");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnWebSocketClose
    public void onClose(int statusCode, String reason) {
        System.out.println("WebSocket Closed. Code:" + statusCode);
    }
}

This is the jetty server configured with TLS. Pass a SslConnectionFactory when creating the connector to enable secure communication. For this sample, I've generated a keystore and truststore using java keytool and placed them in src/resources.
Call addWebSocket() with your annotated WebSocket pojo to add WebSockets to the server.

package org.amila.sample.websocket.server;

import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.resource.FileResource;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;

import java.util.ArrayList;
import java.util.List;

public class WebSocketServer {
    private Server server;
    private String host;
    private int port;
    private Resource keyStoreResource;
    private String keyStorePassword;
    private String keyManagerPassword;
    private List<Handler> webSocketHandlerList = new ArrayList<>();

    public static void main(String[] args) throws Exception {
        WebSocketServer webSocketServer = new WebSocketServer();
        webSocketServer.setHost("localhost");
        webSocketServer.setPort(8443);
        webSocketServer.setKeyStoreResource(new FileResource(WebSocketServer.class.getResource("/keystore.jks")));
        webSocketServer.setKeyStorePassword("password");
        webSocketServer.setKeyManagerPassword("password");
        webSocketServer.addWebSocket(MyWebSocket.class, "/");
        webSocketServer.initialize();
        webSocketServer.start();
    }

    public void initialize() {
        server = new Server();
        // connector configuration
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStoreResource(keyStoreResource);
        sslContextFactory.setKeyStorePassword(keyStorePassword);
        sslContextFactory.setKeyManagerPassword(keyManagerPassword);
        SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString());
        HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(new HttpConfiguration());
        ServerConnector sslConnector = new ServerConnector(server, sslConnectionFactory, httpConnectionFactory);
        sslConnector.setHost(host);
        sslConnector.setPort(port);
        server.addConnector(sslConnector);
        // handler configuration
        HandlerCollection handlerCollection = new HandlerCollection();
        handlerCollection.setHandlers(webSocketHandlerList.toArray(new Handler[0]));
        server.setHandler(handlerCollection);
    }

    public void addWebSocket(final Class<?> webSocket, String pathSpec) {
        WebSocketHandler wsHandler = new WebSocketHandler() {
            @Override
            public void configure(WebSocketServletFactory webSocketServletFactory) {
                webSocketServletFactory.register(webSocket);
            }
        };
        ContextHandler wsContextHandler = new ContextHandler();
        wsContextHandler.setHandler(wsHandler);
        wsContextHandler.setContextPath(pathSpec);  // this context path doesn't work ftm
        webSocketHandlerList.add(wsHandler);
    }

    public void start() throws Exception {
        server.start();
        server.join();
    }
    public void stop() throws Exception {
        server.stop();
        server.join();
    }

    public void setHost(String host) {
        this.host = host;
    }
    public void setPort(int port) {
        this.port = port;
    }
    public void setKeyStoreResource(Resource keyStoreResource) {
        this.keyStoreResource = keyStoreResource;
    }
    public void setKeyStorePassword(String keyStorePassword) {
        this.keyStorePassword = keyStorePassword;
    }
    public void setKeyManagerPassword(String keyManagerPassword) {
        this.keyManagerPassword = keyManagerPassword;
    }

}

And finally the client code. WebSocket is included as an inner class. Pass a SslContextFactory when creating the client and sure "wss" as the protocol prefix of the URL.

package org.amila.sample.websocket.client;

import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class JettyWebSocketClient {

    public static void main(String[] args) throws IOException, URISyntaxException {
        new JettyWebSocketClient().run(new URI("wss://localhost:8443/"));
    }
    
    public void run(URI destinationUri) throws IOException {

        SslContextFactory sslContextFactory = new SslContextFactory();
        Resource keyStoreResource = Resource.newResource(this.getClass().getResource("/truststore.jks"));
        sslContextFactory.setKeyStoreResource(keyStoreResource);
        sslContextFactory.setKeyStorePassword("password");
        sslContextFactory.setKeyManagerPassword("password");
        WebSocketClient client = new WebSocketClient(sslContextFactory);
        MyWebSocket socket = new MyWebSocket();
        try {
            client.start();
            ClientUpgradeRequest request = new ClientUpgradeRequest();
            System.out.println("Connecting to : " + destinationUri);
            client.connect(socket, destinationUri, request);
            socket.awaitClose(5, TimeUnit.SECONDS);
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            try {
                client.stop();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @WebSocket
    public class MyWebSocket {
        private final CountDownLatch closeLatch = new CountDownLatch(1);

        @OnWebSocketConnect
        public void onConnect(Session session) {
            System.out.println("WebSocket Opened in client side");
            try {
                System.out.println("Sending message: Hi server");
                session.getRemote().sendString("Hi Server");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @OnWebSocketMessage
        public void onMessage(String message) {
            System.out.println("Message from Server: " + message);
        }

        @OnWebSocketClose
        public void onClose(int statusCode, String reason) {
            System.out.println("WebSocket Closed. Code:" + statusCode);
        }

        public boolean awaitClose(int duration, TimeUnit unit) throws InterruptedException {
            return this.closeLatch.await(duration, unit);
        }
    }

}

Thursday, September 27, 2012

Creating OSGi Bundles with Maven Bundle Plugin

OSGi is a framework specification to create java applications based on a complete and dynamic component model. That means your application can be entirely consisted of cohesive and loosely-coupled modules. These modules are called "bundles" in OSGi jargon.

There are several implementations including Apache Felix and Eclipse Equinox. These implementations use existing capabilities of Java Archive (jar) files to implement OSGi bundles. If you look inside a general jar file (which is basically a zip file with .jar extension), there will be a file called MANIFEST.MF inside a directory named META-INF. If the jar was built by maven it will contain some meta data like: 
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: amilas
Build-Jdk: 1.6.0_35


There are manifest headers specific to OSGi so that runtime can recognize the jar file as a bundle ( and more). Bnd is a tool used to insert those headers into jar files.

While there's built-in support for OSGI in famous IDEs such as Eclipse and IntelliJ IDEA, if your project is maven-based, it's convenient to use maven for creating bundles too.
Maven Bundle Plugin can be used for this purpose. We can specify OSGi parameters in the module POM and the bundle plugin will use Bnd to automatically insert them and create an OSGi bundle.

This is a introductory post to show how to use Maven with IntelliJ IDEA to create OSGi bundles.

Goto File-> New Project... and select "Create  project from scratch"
At the next screen, enter give a project name and a location as shown. Make sure to select the type as Maven Module.



Finally enter a group id as shown, and click finish.

This will create a new project and a module from maven archtype.

Now let's create two sub modules for our project. these will be the resulting OSGi bundles.

Goto File-> Add Module... again "Create  project from scratch" and name the module as "module-a". In the final screen "Add module to" and "parent" fields should be automatically filled as shown.


Similarly add another module named module-b

 Create two packages "api" and "impl" inside org.amila.sample.osgi.a in module-a, and similarly in module-b so the project pane will show the project structure as follows.

You can use Bundle activator to perform some tasks when the OSGi framework starts or stops a certain bundle. We can implement org.osgi.framework.BundleActivator interface to utilize this function.
Create a new class Adapter in your impl package in module A. Use the following code:

package org.amila.sample.osgi.a.impl;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

    /**
     * Implements BundleActivator.start().
     * @param bundleContext - the framework context for the bundle.
     **/
    @Override
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("Module A is starting");
    }

    /**
     * Implements BundleActivator.stop().
     * @param bundleContext - the framework context for the bundle.
     **/
    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        System.out.println("Module A is shutting down");
    }
}

Add the felix dependency to the module POM.


Set the packaging to "bundle" and add the maven bundle plugin.

After these changes, pom.xml of module-a should look like this:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>OsgiDemo</artifactId>
        <groupId>org.amila.sample.osgi</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>module-a</artifactId>
    <packaging>bundle</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <version>2.3.7</version>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
                        <Bundle-Name>${project.artifactId}</Bundle-Name>
                        <Bundle-Version>1.0.0</Bundle-Version>
                        <Bundle-Activator>org.amila.sample.osgi.a.impl.Activator</Bundle-Activator>
                        <Private-Package>org.amila.sample.osgi.a.impl</Private-Package>
                   </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.felix</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

</project>

Similarly add Activator and bundle configuration for module B.

 Now, you can build the project using maven. Resulting jar files will be OSGi bundles. If you open MANIFEST.MF file of a bundle now it will contain bundle headers as we configured in the module POM.

You don't have to specify packages to import. If your module uses a package from another module, its import header will be automatically added to the manifest.mf file.

Wednesday, February 1, 2012

Using Apache Thrift with Maven

When you use Thrift for your project, you have to manually generate the sources, put them inside your source folder and build.

This plugin does the work for you when you execute the maven build.

However, this plugin is not yet available on maven central repositories, therefore you have to add developer's repo. to your project pom.

 

Then, as described in the plugin's project page, you have to:

1. Use Java 1.5 or newer due to the usage of Generics

You can specify it in the configuration of maven compiler plugin:

 


2. Have Thrift executable is in your PATH or set the parameter of the plugin to the correct location.

 

3. Include the dependency for libthrift

 

Then place your *.thrift files to the directory: src/main/thrift.

Now you can execute mvn clean install as usual.

Tuesday, March 15, 2011

Building WSO2 Carbon from Source





  1. Introduction
  2. Required(and recommended) tools
  3. Setting up
  4. Getting the source
  5. Building
  6. Tips/Issues/Fixes

1.Introduction


Carbon is the core platform of complete enterprise middleware stack by WSO2 which is completely open source.
Read more here: http://wso2.org/



2.Required Tools

Linux
Sun Java Development Kit 1.6
Apache Maven 2.2
Apache Ant 1.8
Subversion 1.6

3.Setting up

You have to be online.

a.Install java:

Goto terminal

Allow the download and accept the terms when done.
Environment variables will be automatically created for you by the installer.
Verify if it's correctly installed by


b.Install Maven

Ant also should be installed automatically by maven.
Verify if it's correctly installed by


c.Install Subversion

Verify if it's correctly installed by


d.Setting Maven option for permanent generation space

PermSize defines the size for the Permanent Generation which holds meta-data describing user classes. The Permanent Generation (PermGen) Space is set to a small value by default, by the JDK (64 MB maximum for JDK 1.5). Large applications such as Carbon with a large code base need this set to a higher value.
You need to set it in the environment variable MAVEN_OPTS
You can also increase the java heap size

Better approach is to set these in .bashrc file in your home so the changes will not be lost when you begin another session (or terminal)

Add the above too lines to the end of the file and save.

4.Getting the source
create the folder where you want to get sources.
for example, if you want it in "carbonSource"

then get the source from wso2 subversion repository by,

Wait until all the sources are downloaded. this includes all the dependencies
of carbon including several apache projects as well as all the carbon based
products. Therefore it's going to take a while.

5. Building
if you go inside the carbon directory, you can see





You can build all things by just using:

That will compile the sources, build all dependencies, perform tests and build jar files.
But probably it will not work straightaway, specially in the first try. Also, testing
take a long time. We don't need to test everything when building for the first time.
Therefore we can skips the tests by setting the variable maven.test.skip to true.
You should build in the following order:
  • dependencies
  • orbit
  • service-stubs
  • core
  • components
  • features
  • products
For example, to build dependencies:

When building, maven will automatically download files for plugins etc. needed
they are downloaded to local maven repository located at your home's /.m2/repository/

After you have successfully built dependencies, orbit, core, components and features, you can move on to building carbon
 
 
You can also build other products if you like.


6.Tips/Issues/Fixes

Make sure you have plenty of time and bandwidth before starting to build
After building carbon for the first time, my m2 repository had 2GiB of downloaded content.
Skip tests when you build for the first time
If your build fails halfway, when you build again, use no plugin update flag (npu).
When it's used, maven won't check for updates on already downloaded packages,
only download missing packages, so it will speed up the process.



Possible Issues & Fixes
==================


Sometimes build may fail complaining about missing artifact. Make sure your connection is working properly. Try again in a bit. If the missing artifact's has the "SNAPSHOT" part, it's a local file. Try to find the it's package using find command and build it from inside the particular package.
Compilation errors might occur rarely since carbon is under active development. If so, try updating the sources


You may also get the error:


This happens due to insufficient permanent generation space as described previously.
Verify you have correctly set maven options to increase permgen space by,
If it isn't correctly set, see the section "Setting Up" to set the variable.

Another possible error:
Increase the maximum number of open files to fix this
Add these two lines to end of the file

You can save the terminal output for later reference with tee command
For example:

Good Luck!