Skip navigation

Category Archives: Intermediate


For quite some time I’ve been looking for a very easy way to deploy a small service using Java that is accessible through HTTP and self-hosted, or in other words, it doesn’t need a installed server. I was initially thinking about using embedded Tomcat or Jetty by writing the server code manually.

But then it turns out that there is this project from Spring built specifically for this kind of application: the Spring Boot. As quoted from the web,

Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that can you can “just run”.

By using this framework, I only need to focus on building the service rather than writing the HTTP server. And running the service is just as simple as using plain old `java -jar`.

The whole source code can be seen in this URL https://github.com/petrabarus/springboot-opsworks-example.

I started by using the sample code from Spring Boot.

import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
@EnableAutoConfiguration
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleController.class, args);
    }
}

The skeleton code for the controller can not be much simpler than this.

And here’s the POM file.

<?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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>net.petrabarus.maleskoding</groupId>
    <artifactId>springboot-opsworks-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <!-- Sets to use Jetty. No particular reasons -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <!-- For deployment ready -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <!-- Creates directory for OpsWorks config -->
                <directory>src/deploy</directory>
                <targetPath>../deploy</targetPath>
            </resource>
            <resource>
                <!-- Creates config directory for external config --> 
                <directory>src/config</directory>
                <targetPath>../config</targetPath>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <!-- Creates JAR -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <!-- Creates zip distributable -->
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <descriptor>src/assembly/dep.xml</descriptor>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>
</project>

The XML code below is the minimum configuration to run the service. (The spring-boot-starter-jetty is used to make the service runs using Jetty instead of the default embedded Tomcat).

<!-- cut -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <!-- Sets to use Jetty. No particular reasons -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>

<!-- cut -->

To build the JAR, I use this plugin

<!-- cut -->
    <build>
        <!-- cut -->
        <plugins>
            <plugin>
                <!-- Creates JAR -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
<!-- cut -->

Building the JAR is simply just by executing

$mvn clean install

and the JAR will be in the `target` directory, e.g. target/springboot-opsworks-example-1.0-SNAPSHOT.jar I run it using the usual java -jar command

$target/springboot-opsworks-example-1.0-SNAPSHOT

I can see the result by using curl

$curl localhost:8080
Hello World!

Now it’s time to deploy it using OpsWorks. In this example I use `HTTP archive`, but it’s better to use `S3 archive` to provide better security since it can use IAM credentials.

Screenshot from 2014-09-20 00:02:21

The ZIP-ed file for deployment can be created using `spring-boot-maven-plugin`

<!-- cut -->
    <build>
        <!-- cut -->
        <plugins>
            <plugin>
                <!-- Creates zip distributable -->
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <descriptor>src/assembly/dep.xml</descriptor>
                </configuration>
            </plugin>
<!-- cut -->

Here’s the assembly XML configuration

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <id>bin</id>
    <formats>
        <format>zip</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>${project.build.directory}</directory>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>*.jar</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}/config</directory>
            <outputDirectory>config</outputDirectory>
            <includes>
                <include>*.xml</include>
                <include>*.properties</include>
                <include>*.yml</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}/deploy</directory>
            <outputDirectory>deploy</outputDirectory>
        </fileSet>
    </fileSets>
</assembly>

Now I can have a very simple build and distribution script like below. This can be use for Jenkins. When a new change is pushed to the git repository, Jenkins will download the change, build, test the source, and then pack and upload the binary.

$mvn clean install assembly:single && s3cmd put -P target/*.zip s3://path/to/the/upload.zip

As we know, deploying in OpsWorks is just a matter of click.

Screenshot from 2014-09-20 00:02:07

Note that deploying the OpsWorks app doesn’t mean running the service automatically. For that I need to use a deploy hook. Chef provides a way to execute a ruby script on steps in the deployment. Using the `before_restart.rb` I can execute a task that happens after the app directory changes its link from the old directory to the new deployed directory.

The content of `before_restart.rb` script is in below

script "runjar" do
    interpreter "bash"
    user "root"
    cwd release_path
    code <<-EOH
        java -jar *.jar > /var/log/springbootext1/app.log 2>&1 &
    EOH
end

I added the file in the `src/deploy` directory and I declare it as a resource in the POM file.

    <build>
        <resources>
            <resource>
                <!-- Creates directory for OpsWorks deploy hook -->
                <directory>src/deploy</directory>
                <targetPath>../deploy</targetPath>
            </resource>

The next question is how to shutdown the running service and update the new JAR. Spring Boot provides a production-ready feature called the actuator. Here I can put a shutdown for the service that will turn off the running service. To add the actuator, I define the dependency in the POM file.

<!-- cut -->
        <dependency>
            <!-- For deployment ready -->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
<!-- cut -->

And in the deploy hook, before the run service script I added new script

script "shutdown" do
    interpreter "bash"
    user "root"
    cwd release_path
    code <<-EOH
        curl -XPOST "http://localhost:8080/shutdown" > /var/log/springbootext1/app.log 2>&1
    EOH
end

That script send a POST request to the current running service to shut itself down. As default the shutdown actuator is not enabled by default. I still need to add new configuration. To do this, I added a new file `scr/config/application.properties` and I added this line.

endpoints.shutdown.enabled=true

Spring Boot has a very flexible way of configuring the application. The simplest way is to have `config/application.properties` file in the JAR directory. The application JAR will check what’s inside the file and override the configuration.

The deploy directory also needs to be declared as resource in the POM file

<!-- cut -->
    <build>
        <resources>
            <!-- cut -->
            <resource>
                <!-- Creates config directory for external config --> 
                <directory>src/config</directory>
                <targetPath>../config</targetPath>
            </resource>
        </resources>
            <!-- cut -->

So now I have a minimum skeleton for deploying a small standalone HTTP service. Keep in mind to restrict the 8080 port by using AWS Security Group so that the 8080 port is only accessible to appropriate instances.

Regarding the deployment, I’m guessing there will be few seconds downtime between the shutdown and running new service. This can be even much longer if I put some long task in the shutdown listener. I’m still thinking how to do this more seamlessly. Ideas needed 😉

Another thing I can do is passing the custom JSON in the OpsWorks to the `application.properties` configuration file.

I can define something like this.

{
    "springboot-example": {
        "config": {
            "server.port" : 3333,
            "management.port": 3334
        }
    }
}

It’s a good idea to make the server port and the management port more configurable among other things. And I can write that JSON in the `application.properties` using the deploy hook `before_symlink.rb` and modify the `before_restart.rb` to use the custom JSON for the port numbers. This is very flexible since I can define the custom JSON in the Stack level and also in the deployment level.

Iklan

Setelah sekian lama terbengkalai, akhirnya saya melanjutkan postingan ini. Di postingan tersebut (dan postingan-postingan sebelumnya), saya menulis tentang bagaimana mengkustomisasi CJuiAutoComplete sesuai dengan kebutuhan. Tapi kelemahan dari implementasi sebelum-sebelumnya adalahautocompletetersebut terikat pada satucontroller dan agak repot untuk dipakai diview yang lain.

Agar dapat dengan mudah digunakan di view yang lain solusinya adalah dengan membungkus autocomplete tersebut menjadi sebuah Widget. Dengan demikian, untuk menggunakan autocomplete, kita cukup meletakkan kode widgetnya saja. Permasalahan dengan Widget ini adalah bagaimana cara mendapatkan data untuk autocomplete tersebut? Caranya mudah, yakni dengan menggunakan fitur ActionProvider pada Widget.

Read More »


Saya sangat senang menggunakan XAMPP baik itu di Windows maupun Linux karena instalasi yang sangat simpel. Apalagi jika digunakan di Linux, kita tidak perlu pusing-pusing menjalankan perintah-perintah instalasi (apalagi juga mengcompile source code). Cukup download paket programnya, kemudian extract di direktori yang diinginkan.

Hanya saja belakangan saya membutuhkan memcached untuk instalasi XAMPP di komputer saya untuk mengetes aplikasi web saya yang menggunakan memcached. Sebenarnya bisa tinggal instalasi php extension untuk memcached dari PECL.

$/opt/lampp/bin/pecl install memcache

Masalahnya ini tidak bisa langsung dilakukan karena ada perbedaan arsitektur antara XAMPP dengan komputer saya. XAMPP for Linux (atau sering disebut LAMPP) yang saya instal menggunakan arsitektur 32 bit (sepertinya memang hanya disediakan yang 32 bit) sementara komputer saya menggunakan Ubuntu 64bit. Meskipun berhasil diinstal tetapi nanti tidak bisa dijalankan.

Read More »


Melanjutkan postingan sebelumnya, sekarang tampilan dropdown dari CJuiAutoComplete yang hanya teks sederhana akan saya ubah menjadi lebih bagus. Dropdown ini akan kita buat agar bisa menampilkan data selain nama panjang, tetapi juga mungkin foto profil atau asal kota. Nantinya akan mirip dengan search autocomplete yang ada di Facebook seperti di bawah.

Autocomplete di Facebook search.

Read More »


Biar saya tidak lupa. Sekarang saya menggunakan fitur geospatial dari MySQL yang menyediakan fitur geospatial indexing. Dengan fitur ini, saya bisa menyimpan data-data berupa lokasi pada bumi (dengan menggunakan latitude dan longitude) dan bisa melakukan kueri berdasarkan posisi sebuah lokasi.

Tapi meskipun ada banyak fitur-fitur kueri berdasarkan lokasi yang disediakan oleh MySQL, sayangnya tidak ada fungsi bawaan untuk mencari lokasi-lokasi dalam jarak tertentu. Dari referensi (lihat di bagian paling bawah tulis) saya mendapatkan kode di bawah ini. Kode ini merupakan implementasi fungsi haversine.

DELIMITER $$
 CREATE FUNCTION distance (a POINT, b POINT) RETURNS double DETERMINISTIC
   BEGIN
     RETURN 6371 * 2 * ASIN(SQRT(POWER(SIN(RADIANS(ABS(X(a)) - ABS(X(b)))), 2) + COS(RADIANS(ABS(X(a)))) * COS(RADIANS(ABS(X(b)))) * POWER(SIN(RADIANS(Y(a) - Y(b))), 2)));
   END  $$
DELIMITER ;

*update* Saat menulis ini merasa agak aneh dengan fungsi di atas, karena fungsi di atas sama sekali tidak memerlukan geospatial indexing. Ternyata ada alternatif yang menggunakan geospatial indexing yakni seperti di bawah.

DELIMITER $$
 CREATE FUNCTION distance (a POINT, b POINT) RETURNS double DETERMINISTIC
  BEGIN
  RETURN GLength(
   LineStringFromWKB(
     LineString(
       a, b
     )
   )
 ) * 100;
  END  $$
DELIMITER ;

Dan data-data lokasi dalam jarak tertentu (dalam satuan kilometer) dari sebuah titik dapat didapatkan dengan kueri seperti di bawah. Voila!

SELECT id, name, latitude, longitude, distance(coordinate, POINT(-6.890469, 107.610244)) AS cdist
FROM Locations
HAVING cdist < 10
ORDER BY cdist LIMIT 10;

Referensi utama dari blog ini dapat dilihat di


Salah satu aplikasi yang sedang saya kembangkan menggunakan username vanity URL. Maksudnya adalah pada setiap pengguna akan mempunyai sebuah link yakni username pengguna yang ditempelkan pada domain. Contohnya adalah seperti twitter. URL twitter saya adalah http://www.twitter.com/petrabarus. Pada URL tersebut username saya petrabarus ditambahkan pada domain twitter.com. Sekarang kita ingin membuat sebuah aplikasi yang dapat mengakses user saya petrabarus dengan alamat http://aplikasi/petrabarus.

Sebelum Yii 1.1.8, implementasi ini agak sedikit kompleks (saya tidak tertarik untuk membahasnya). Tapi sejak adanya kelas CBaseUrlRule, ini jadi lebih mudah.

Pertama-tama, misalnya kita mempunyai kelas model User yang digenerate dari Gii. Saya yakin hampir semua aplikasi Yii memiliki kelas seperti di bawah.

/*
* The followings are the available columns in table 'Users':
* @property integer $id
* @property string $username
* @property string $email
*/
class User extends CActiveRecord {
}

Dengan adanya ActiveRecord class bawaan Yii, kita dapat dengan mudah mengkueri user berdasarkan usernamenya

$username = 'petrabarus';
User::model()->find('username = :username', array(':username' => $username));

Kemudian kita mempunyai sebuah controller untuk melihat user tersebut

class UserController extends CController {
	public function actionView(){
		$username = $_GET['username'];
		$model = User::model()->find('username = :username', array(':username' => $username));
		$this->render('view', array('model' => $model));
	}
}

Untuk mengakses URLnya biasanya yang kita lakukan adalah mengakses http://aplikasi/user?username=petrabarus.

Sekarang kita tinggal membuat sebuah URL Rule seperti di bawah

class UserUrlRule extends CBaseUrlRule {

	public $connectionID = 'db';

	public function createUrl($manager, $route, $params, $ampersand) {
		if ($route === 'user/view' && isset($params['username'])) {
			$newroute = $params['username'];
			unset($params['username']);
			if (count($params) > 0) {
				$newroute.= '?' . http_build_query($params);
			}
			return $newroute;
		}
		return false;
	}

	public function parseUrl($manager, $request, $pathInfo, $rawPathInfo) {
		if (preg_match('/[a-zA-Z0-9\.]+/', $pathInfo, $matches)) {
			$username = $matches[0];
			$exists = User::model()->exists('username = :username', array(':username' => $username));
			if ($exists) {
				$_GET['username'] = $username;
				return 'user/view';
			} else
				return false;
		}
		return false;
	}

}

Method parseUrl dibutuhkan untuk memparsing url yang diakses melalui address bar browser. Sementara itu method createUrl adalah untuk membentuk URL yang kita inginkan. Method createUrl ini nantinya akan digunakan oleh method createUrl milik Controller. Pada method parseUrl itu saya memfilter URL tersebut dengan regex /[a-zA-Z0-9\.]+/ yang merupakan pattern rule dari username pada aplikasi saya. Kemudian jika ada yang mirip maka string pada URL tersebut akan saya periksa apakah ada user yang memiliki nama yang sama. Jika ada maka request dialihkan ke Controller user/view.

Perlu diperhatikan, agar username tidak bentrok dengan URL-URL yang dimiliki oleh Controller ada baiknya kita mempunyai daftar username yang diblacklist, yang diantaranya adalah nama-nama Controller seperti misalnya login, logout, signin, signout, register, dan lain-lain.

Lalu kita tambahkan ke rule di konfigurasi.

'urlManager' => array(
	    'urlFormat' => 'path',
	    'showScriptName' => false,
	    'rules' => array(
		array(
		    'class' => 'application.components.UserUrlRule',
		    'connectionID' => 'db',
		),
		'<controller:\w+>/<id:\d+>' => '<controller>/view',
		'<controller:\w+>/<action:\w+>/<id:\d+>' => '<controller>/<action>',
		'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
	    ),
	),

Dan sekarang kita bisa mengakses halaman user dari http://aplikasi/petrabarus. Untuk mendapatkan URL ini pada Controller, kita cukup memanggil

$this->createUrl('user/view', array('username' => 'petrabarus'));

Crash Failure dan Crash Recovery adalah 2 buah model di dalam bidang sistem terdistribusi. Di dalam crash failure ketika sebuah proses mengalami crash maka proses tersebut tidak akan dapat beroperasi kembali di dalam sistem. Model ini adalah model pertama yang diperkenalkan ketika belajar mengenai algoritma terdistribusi. Akan tetapi di dalam kenyataannya, proses-proses dapat kembali bekerja setelah mengalami crash.

Di dalam model Crash Recovery, sebuah proses miliki dua buah keadaan yakni up dan down. Up adalah keadaan di mana proses sedang beroperasi dan sebaliknya down adalah keadaan di mana proses sedang tidak beroperasi. Perubahan keadaan dari up menjadi down disebut crash dan perubahan sebaliknya disebut recovery. Sebuah proses dikatakan always up jika di dalam melakukan aktivitasnya proses tidak pernah sekalipun mengalami down hingga selesai. Di sisi lain, ada sebuah keadaan yakni unstable jika proses tersebut mengalami crash dan recovery secara berturut-turut dalam jumlah yang sangat banyak. Ketika itu proses dianggap tidak beroperasi dengan baik.

Berbeda pada model Crash Failure, pada Crash Recovery sebuah correct process adalah proses yang pada suatu waktu akan up secara permanen (dalam hal ini dianggap memiliki waktu yang cukup untuk menyelesaikan aktivitasnya). Sedangkan sebuah faulty process adalah proses yang pada suatu waktu akan always down atau unstable.

Di dalam melakukan aktivitasnya sebuah proses akan dilengkap dengan 2 buah tempat penyimpanan: volatile memory dan stable storage. Ketika sebuah proses mengalami crash maka data yang disimpan pada volatile memory akan terhapus sedangkan data yang disimpan pada stable storage akan tetap utuh. Oleh karena itu stable storage ini digunakan untuk mengembalikan state atau data-data yang diperlukan pada saat proses melakukan recovery.


Mungkin gw udah cerita di beberapa post lalu kalau pada semester ini gw mengambil mata kuliah Sistem Rekognisi. Permasalahan yang dihadapi kali ini adalah bagaimana menipiskan ketebalan sebuah gambar pada karakter yang ingin direkognisi. Penipisan ini dilakukan untuk mengambil fitur-fitur penting dari karakter tersebut. Algoritma ini disebut thinning. Keterangan lebih lanjut bisa dibaca di sini.

Algoritma yang gw gunakan di sini adalah algoritma yang dikemukakan oleh Zhang-Suen dalam paper mereka yang berjudul “A Fast Parallel Algorithm For Thinning Digital Pattern“. Read More »


Kemarin saya dijebak diminta untuk mengisi pelatihan Java untuk OSUM UPI. Saya membawa sesi terakhir dari 6 sesi pelatihan. Pelatihan ini diadakan setiap Sabtu selama 3 minggu di mana setiap hari terdapat 2 sesi. Pelatihan ini ditujukan bagi mahasiswa UPI dan untuk tingkat pemula. Awalnya saya dijebak diminta untuk mengisi materi Netbeans Platform. Tapi agaknya terlalu jauh melompat tingkatnya. Akhirnya saya menawarkan mengisi materi konkurensi di Java.

Materi yang saya bawakan adalah pengantar konkurensi. Saya mengenalkan tentang Thread dan fitur-fitur manajemen Thread yang disediakan oleh Java seperti sleep, interrupt, join, dan wait (yang terakhir ditambahkan di detik-detik terakhir). Selain itu saya juga mengenalkan metode untuk sinkronisasi antar Thread. Dan terakhir saya menyinggung sedikit mengenai Timer.

Modul pelatihan dan slide presentasi dapat dilihat di bawah.

Read More »


*Maaf baru posting setelah sekian lama*

Sekarang saya akan mencoba membuat kelas Entity Persistence pada project yang pernah diposting sebelum ini. Pada proyek itu telah dibuat Persistence Unitnya.
Read More »

%d blogger menyukai ini: