2015年12月30日 星期三

Message Queue Intro & Spring RabbitMQ Example

http://villebez.logdown.com/posts/2015/12/21/message-queue-intro-spring-rabbitmq-example
進入這次主題之前,先讓大家認識
什麼是 Message Queue, Wikipedia.
在電腦科學中,訊息佇列(英語:Message queue)是一種行程間通信或同一行程的不同執行緒間的通信方式,軟體的貯列用來處理一系列的輸入,通常是來自使用者。訊息佇列提供了非同步的通信協定,也就是說:訊息的傳送者和接收者不需要同時與訊息佇列互交。訊息會儲存在佇列中,直到接收者取回它。
訊息佇列(Message Queue,簡稱MQ),從字面意思上看,本質是個佇列,FIFO先入先出,只不過佇列中存放的內容是message而已。其主要用途:不同行程Process/執行緒Thread之間通信。
最初起源于金融系統,用於在分散式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。
訊息中介軟體主要用於元件之間的解耦,訊息的發送者無需知道訊息使用者的存在,反之亦然。
接著要準備這次的環境,下載安裝server,RabbitMQ
RabbitMQ Server Commands,詳見手冊
基本上windows上安裝方式就是下一步、下一步就好了,
安裝完可以連看看RabbitMQ Management Page,輸入default user
Management Page就不詳述了,自己玩玩看吧。
http://localhost:15672/
default username/password:guest/guest
server安裝完後就可以進入這次的主題
其實Spring AMQP管網已經有兩個簡易範例了,一個是純Java,一個是使用Spring的方式
我是使用Spring的方式,做些微調整,並記錄下須注意的地方
首先New Gradle Project,設定dependencies
dependencies {
compile 'org.springframework.amqp:spring-rabbit:1.5.3.RELEASE'
}
Spring Context Config (applicationContext.xml)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <rabbit:connection-factory id="connectionFactory"
        host="127.0.0.1" port="5672" username="guest" password="guest" />

    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
        exchange="myExchange" />
 
    <rabbit:admin connection-factory="connectionFactory" />

  <rabbit:queue name="myQueue" />

    <rabbit:fanout-exchange name="myExchange">
        <rabbit:bindings>
            <rabbit:binding queue="myQueue" />
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <rabbit:listener-container id="rabbitMQContainer"
        connection-factory="connectionFactory" auto-startup="false">
        <rabbit:listener ref="messageHandler" queue-names="myQueue" />
    </rabbit:listener-container>

    <bean id="messageHandler" class="MessageHandler"/>
</beans>


注意設定xmlns:rabbit以及xsi:schemaLocation (http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd)
auto-startup預設為true,也就是spring載入後consumer listener就會啟動並且keep alive
與官網做法不同rabbit:listener沒有設定method屬性,後面說明

Message Handler
public class MessageHandler implements MessageListener {
    public void onMessage(Message message) {
        System.out.println(message);
    }
}
這裡跟官網用法不同,是去實作org.springframework.amqp.core.MessageListener
所以Spring Content Config並沒有設定rabbit:listener的method屬性,
好處是可以拿到更多Message的資訊,不是只有Body的字串,
要用哪種方式沒有對錯,單看需求而定!!
Output Message Object
(Body:'Test'; ID:null; Content:text/plain; Headers:{}; Exchange:myExchange; RoutingKey:; Reply:null; DeliveryMode:PERSISTENT; DeliveryTag:1)
Main
public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        AmqpTemplate amqpTemplate = ctx.getBean("amqpTemplate", AmqpTemplate.class);
        amqpTemplate.convertAndSend("Test");
}

2015年9月24日 星期四

AngularJS Breadcrumb With Route

同步發布於 http://villebez.logdown.com/posts/2015/07/20/angularjs-breadcrumb
AngularJS v1.4.4
Project結構:
-js/app.js
-js/route.js
-js/breadcrumb/directive.js
-js/breadcrumb/factory.js
-js/vendors/angular-route.min.js
-js/vendors/angular.min.js
-template/breadcrumb.html
-template/hello.html
-template/world.html
-template/index.html
-index.html
第一次寫AngularJS文章就挑戰Breadcrumb,寫得非常簡單陽春,
在寫這篇找資料時,意外找到一個更好的做法,提供參考
Auto-breadcrumbs with angular-ui-router
不過我的作法是手動breadcrumbs 冏rz...
那就來獻醜了...
首先呢~當然是看index.html
引入所需要的JS,body就只放breadcrumb directive以及view。

 data-ng-app="bcapp">

 charset="utf-8">
</span>BreadCrumb Demo<span class="nt" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210) !important; font-weight: bold !important;">






-directive></breadcrumb-directive>
data-ng-view>
接著看看app.js、route.js
app 就只有迴圈設定$routeProvider,將route config抽出來至route.js
(function() {
    'use strict';
    angular.module('bcapp', [ 'ngRoute', 'components.breadcrumb' ]).config(configure);
    configure.$inject = [ '$routeProvider' ];
    function configure($routeProvider) {

        // Url 設定
     for ( var locationPath in appConfig.locationConfig) {
            var urlPattern = locationPath;
            var config = appConfig.locationConfig[locationPath];
            $routeProvider.when(urlPattern, config);
        }
    }
})();
route config簡單的設定urlPattern、template與resolve對該頁設定breadcrumb
window.appConfig = window.appConfig || {};
window.appConfig.locationConfig = {
    '/': {
        templateUrl: '/template/index.html',
        resolve : [["Breadcrumbs",function(Breadcrumbs){
            Breadcrumbs.init();
            
        }]]
    },
    '/hello': {
        templateUrl: '/template/hello.html',
        resolve : [["Breadcrumbs",function(Breadcrumbs){
            Breadcrumbs.init();
            Breadcrumbs.push({
                 "name": "Hello",
            });
        }]]
    },
    '/world': {
        templateUrl: '/template/world.html',
        resolve : [["Breadcrumbs",function(Breadcrumbs){
            Breadcrumbs.init();
            Breadcrumbs.push({
                 "name": "World",
            });
        }]]
    }
};
再來看看breadcrumb directive.js, factory.js
directive很基本,指定restrict,載入templateUrl,注入Breadcrumbs factory並set scope.breadcrumbs讓html可以使用這個factory
(function(){
    'use strict';
    angular.module('components.breadcrumb',[]).directive('breadcrumbDirective', Directive);
    
    Directive.$inject = 
        ['Breadcrumbs'];
    
    function Directive(Breadcrumbs) {
        var directive = {
            scope: {
                
            },
            restrict:    'E',
            templateUrl : '/template/breadcrumb.html',
            link : link  
        };
        
        return directive;
        
        function link(scope, elem, attrs, ctrl) {
            scope.breadcrumbs = Breadcrumbs;
        }
    }
})();
factory提供了幾個function
init初始化
set傳入Array設定整個breadcrumb
push傳入一筆資料進入Array
getAll取得整個Array
getFirst拿到第一筆資料
(function(){
    'use strict';
    angular
        .module('components.breadcrumb')
        .factory('Breadcrumbs', factory);

    factory.$inject = [];

    function factory() {
        var self = this;
        self.breadcrumbs  = [];

        // Interface
     self.init = init;
        self.set  = set;
        self.push = push;
        self.getAll = getAll;
        self.getFirst = getFirst;

        // Implement
     function init(){
            self.breadcrumbs =[{
                 "name": "首頁",
                 "path": "#/"
            }];
        }
        
        function set (urls) {
            self.breadcrumbs = urls;
        }
        
        function push (url){
            self.breadcrumbs.push(url);
        }
        
        function getAll(){
            return self.breadcrumbs;
        }
        
        function getFirst(){
            return self.breadcrumbs[0] || {};
        }
        
        return self;
    }
})();
最後就是四個template,其實沒什麼東西,只是為了呈現不同頁面與文字
-template/breadcrumb.html
class="breadCrumb">
  • ng-repeat="breadcrumb in breadcrumbs.getAll()" class="node"> -switch on="$last"> ng-switch-when="true">{{breadcrumb.name }} ng-switch-default> href="{{ breadcrumb.path }}" class="hand">{{ breadcrumb.name }} </ng-switch> ng-if="!$last" class="divider">>
  • -template/hello.html
    Hello
    
    -template/world.html
    world
    
    -template/index.html

    2015年5月22日 星期五

    Resolved : Set the Database Default Value in a Hibernate/JPA Save Persist

    Using JPA (with Hibernate), I was inserting a record in my database. 
    In my table, one column is defined with a DEFAULT Value.
    But when I insert a save, persist my record, JPA put it as NULL.
    So using JPA, the solution was to use insertable = false in the column definition. 
    JPA will ignore then that column while inserting in the Database and the default value will be used.
    @Column(name = "myColumn", insertable = false)

    Spring AOP Around Advice 實作 Exception 處理

    緣由:專案內需要將Exception獨立抽出來處理,為了不影響原有的商業邏輯,並且讓code reuse
    所以第一時間就想到要用AOP實作,順便回去練一下很久沒碰的Spring AOP,並複習一下觀念
    問題:Exception處理
    解決方式:Spring AOP Around Advice
    好處:
    1. 統一錯誤訊息,讓每個工程師各寫各的錯誤處理風格可以得到統一。
    2. 讓其他人可以專注在商務邏輯
    3. 程式看起來比較簡潔乾淨
    基本上我不解釋觀念,因為網路上已經有很多很棒的解說了
    我也還在學習,很多東西其實第一次看完全都不懂,很抽象,
    但隨著時間、經驗的累積,又回去看了第二次第三次,就會越來越有感覺
    spring-aop-diagram.jpgspring-aop-diagram.jpg
    In Spring AOP, 4 type of advices are supported :
    • Before advice – Run before the method execution
    • After returning advice – Run after the method returns a result
    • After throwing advice – Run after the method throws an exception
    • Around advice – Run around the method execution, combine all three advices above.
    ============================= 分隔線 =================================
    我是使用 Around advice ,其實也就是把程式裡散落在各處的try catch集中至ExceptionAdvice統一處理。
    build.gradle
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'maven'
    
    compileJava.options.encoding = 'UTF-8'
    def springVersion = "3.2.6.RELEASE"
    
    dependencies {
        compile 'org.aspectj:aspectjweaver:1.7.1'
        compile 'org.slf4j:jcl-over-slf4j:1.7.1'
        compile 'org.slf4j:slf4j-log4j12:1.7.1'
        compile "org.springframework:spring-aop:$springVersion"
        compile "org.springframework:spring-context:$springVersion"
    }
    
    applicationContext.xml
    
    
        
    
        
        
        
        
            
                
                
            
         
    
    
    com.test.aop.advice.ExceptionAdvice
    public class ExceptionAdvice {
        private static Logger log = LoggerFactory.getLogger(ExceptionAdvice.class);
    
        public Object exceptionHandler(ProceedingJoinPoint proceedingJoinPoint) {
            Object value = null;
    
            try {
                value = proceedingJoinPoint.proceed();
            } catch (Throwable e) {
                log.info(proceedingJoinPoint.getSignature().getName() + " error");
                
                String errorMsg = "內部錯誤:" + e.getMessage();
                String errorCode = "500";
                
                Map resultJsonMap = new HashMap();
                resultJsonMap.put("successful", false);
                resultJsonMap.put("errorCode", errorCode);
                resultJsonMap.put("errorMsg", errorMsg );
    
                value = resultJsonMap;
                log.info(value.toString());
            }
            return value;
        }
    
    com.test.service.TestService
    public class TestService {
        private static ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        public String runTest() {
            throw new IllegalArgumentException("這是測試");
        }
    
        public static void main(String[] args) {
            TestService ts = ac.getBean("testService", TestService.class);
            ts.runTest();
        }
    }
    
    參考資料