2017年4月26日 星期三

WebSphere 使用xml設定部屬的專案

有些應用程式在佈署到WebSphere的時候需要調整一些設定

目前我碰到的是母類別最後(PARENT_LAST)


是不是每次佈署都要改一次這個我沒有去驗證

但這件事是可以靠xml去設定

首先應用程式要弄成EAR檔的形式

xml設定檔的位置如圖


deployment.xml內容如下:
<?xml version="1.0" encoding="UTF-8"?>
<appdeployment:Deployment xmi:version="2.0"
 xmlns:xmi="http://www.omg.org/XMI"
 xmlns:appdeployment="http://www.ibm.com/websphere/appserver/schemas/5.0/appdeployment.xmi"
 xmi:id="Deployment_1297859327856">
  <deployedObject xmi:type="appdeployment:ApplicationDeployment"
   xmi:id="ApplicationDeployment_1297859327856"
   startingWeight="10"
   warClassLoaderPolicy="SINGLE">
    <classloader xmi:id="Classloader_1297859327856" mode="PARENT_LAST"/>
  </deployedObject>
</appdeployment:Deployment>

Spring - CXF 小心得

xml設定檔:
<bean id="XxxBean" class="xxx.XxxBean">

<bean id="ServiceImpl" class="xxx.ServiceImpl" />

<jaxws:endpoint id="Service" implementorClass="xxx.Service" implementor="#ServiceImpl" address="/Service">
    <jaxws:properties>
        <entry key="mtom-enabled" value="true"/>
    </jaxws:properties>
</jaxws:endpoint>


Java:
@MTOM
public interface Service {
    @WebMethod
    public Response xxxMethod();
}


@MTOM
public class ServiceImpl implements Service{
    @Autowired
    private ApplicationContext appContext;

    // 讓bean不為單例改用此寫法
    private XxxBean getXxxBean(){
        return (XxxBean)appContext.getBean("XxxBean");
    }

    @WebMethod
    public Response xxxMethod(){
        return new Response();
    }
}


1.不需要@WebService

這邊要特別註明
很多網站都會在 Java 的 Class 和 interface 上面加@WebService的標籤
其實這個是很不必要的東西
加了會造成這個 Service 有兩個接口

一個是xml檔裡設定的address="/Service"
另一個是@WebService

@WebService有細項參數可以調整,若沒給的話會使用預設值
而address就是其中一個

所以如果沒有打算要開兩個接口的話就不要加@WebService

(如果有讀者發現這段話是因為少設什麼所以才導致這種情形的話希望能回饋給我,感恩!!)

2.scope prototype

ServiceImpl在Spring-cxf的架構下每個request都是使用同一個ServiceImpl Object
這就表示ServiceImpl中使用到@Autowired的Object也會被固定為同一個Object
(無論@Autowired的Object有沒有做設定)

假設有什麼寫法必須使用不同的Object,就只好使用appContext.getBean()的方式
另外Bean要配合設定(擇其一)
XML:scope="prototype"
JAVA CLASS:@Scope("prototype")

3.MTOM

如果有用到DataHandler
在上傳到service這時的inputStream用完的話要記得close
不然會有殘檔在server上
下載會自己關,不用擔心

2017年4月25日 星期二

zip4j 自定義OutputStream

public class ZipUtil{
    /**
     * 由於有zipModel的原因
     * 故不建議使用static
     * */

    private ZipModel zipModel = new ZipModel();

/*
    public static void main(String[] args) {
        ZipUtil util = new ZipUtil();
        try {
            util.doZipEnc();
            System.out.println("成功");
        } catch (Exception e) {
            System.out.println("失敗");
            e.printStackTrace();
        }
    }

    public void doZipEnc() {
        try {
            // 將壓縮流寫到內存
//            ZipOutputStream saos = new ZipOutputStream(new ByteArrayOutputStream(), this.zipModel);
            // 本地測試
            FileOutputStream f = new FileOutputStream("C:/Users/Louis/Desktop/test1.zip" ,true);
            ZipOutputStream saos = new ZipOutputStream(f, this.zipModel);

            for(int i=0;i<2;i++){
                ZipParameters parameters = createZipParameters("test" + i + ".txt", "" + i + i + i);
                addNewStreamToZip(saos, ("test" + i + ".txt").getBytes(), parameters);
            }
//          saos.writeTo(f);

            saos.finish();
            saos.close();
            f.close();
            //文件大小
//          System.out.println("size:" +(saos.toByteArray().length/1024));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
*/
    public ZipOutputStream createZipOutputStream(OutputStream outputStream){
        return new ZipOutputStream(outputStream, zipModel);
    }

    public void addNewStreamToZip(ZipOutputStream outputStream, InputStream data, ZipParameters parameters) throws ZipException {
        ByteArrayOutputStream tempOut = new ByteArrayOutputStream();

        try{
            byte[] buffer = new byte[1024];

            for(int i ; (i = data.read(buffer)) != -1 ; ){
                tempOut.write(buffer, 0, i);
                tempOut.flush();
            }
        }catch (Exception e) {
            throw new ZipException(e);
        }finally{
            try{
                data.close();
                tempOut.close();
            }catch (Exception e) {
            }
        }

        addNewStreamToZip(outputStream, tempOut.toByteArray(), parameters);
    }

    public void addNewStreamToZip(ZipOutputStream saos, byte[] data, ZipParameters parameters) throws ZipException {
        try {
            if (zipModel.getEndCentralDirRecord() == null) {
                throw new ZipException("invalid end of central directory record");
            }
/*
            checkParameters(parameters);

            saos.putNextEntry(null, parameters);

            if (! parameters.getFileNameInZip().endsWith("/") &&
                ! parameters.getFileNameInZip().endsWith("\\")
            ) {
                saos.write(data);
            }
*/
            saos.putNextEntry(null, parameters);

            saos.write(data);

            saos.closeEntry();
        } catch (ZipException e) {
            throw e;
        } catch (Exception e) {
            throw new ZipException(e);
        }
    }

    public ZipParameters createZipParameters(String fileNameInZip, String password){
         ZipParameters parameters = new ZipParameters();

         parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // 壓縮方式
         parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); // 壓縮等級
         parameters.setSourceExternalStream(true);
         parameters.setFileNameInZip(fileNameInZip);

         // 若有多檔,password個別有效
         if(password != null && ! password.trim().isEmpty()) {
             parameters.setEncryptFiles(true);
             parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD); // 加密方式
             parameters.setPassword(password);
         }

         return parameters;
    }
/*
    private void checkParameters(ZipParameters parameters) throws ZipException {

        if (parameters == null) {
            throw new ZipException("cannot validate zip parameters");
        }

        if ((parameters.getCompressionMethod() != Zip4jConstants.COMP_STORE) &&
                parameters.getCompressionMethod() != Zip4jConstants.COMP_DEFLATE) {
            throw new ZipException("unsupported compression type");
        }

        if (parameters.getCompressionMethod() == Zip4jConstants.COMP_DEFLATE) {
            if (parameters.getCompressionLevel() < 0 && parameters.getCompressionLevel() > 9) {
                throw new ZipException("invalid compression level. compression level dor deflate should be in the range of 0-9");
            }
        }

        if (parameters.isEncryptFiles()) {
            if (parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_STANDARD &&
                    parameters.getEncryptionMethod() != Zip4jConstants.ENC_METHOD_AES) {
                throw new ZipException("unsupported encryption method");
            }

            if (parameters.getPassword() == null || parameters.getPassword().length <= 0) {
                throw new ZipException("input password is empty or null");
            }
        } else {
            parameters.setAesKeyStrength(-1);
            parameters.setEncryptionMethod(-1);
        }
    }
*/
}


用法:
ZipUtil zipUtil = new ZipUtil();

ZipOutputStream zipOutputStream = zipUtil.createZipOutputStream(outputStream);

ZipParameters parameters = zipUtil.createZipParameters(fileNameInZip, zipPwd);

zipUtil.addNewStreamToZip(zipOutputStream, input, parameters);

zipOutputStream.finish();
zipOutputStream.close();


第3行這邊可以自由定義需要的OutputStream
第5行
fileNameInZip:在zip檔中檔案的名字
(zip檔中允許相同名字的檔案,有問題的只會發生在解zip的時候一直會問你要不要覆蓋相同的檔案)
zipPwd:密碼是個別有效的

CompareCollectionUtil

使用CompareCollectionUtil有個大前提

Collection內的Class必須實作 hashCode 和 equal
因為原理是使用equal這個方法在比較

public class CompareCollectionUtil {
    /**
     * a = [1, 2, 3]    b = [2, 3, 4]
* 比對兩個List 用 a 去跟 b 比較,回傳缺少的部分
* return [1] **/ public static <T> List<T> deleted(Collection<T> a, Collection<T> b) { if(a == null){ return new ArrayList<T>(); } List<T> result = new ArrayList<T>(a); if(b != null){ result.removeAll(b); } return result; } /** * a = [1, 2, 3] b = [2, 3, 4]
* 比對兩個List 用 a 去跟 b 比較,回傳相同的部分
* return [2, 3] **/ public static <T> List<T> retained(Collection<T> a, Collection<T> b) { if(a == null || b == null){ return new ArrayList<T>(); } List<T> result = new ArrayList<T>(a); result.retainAll(b); return result; } /** * a = [1, 2, 3] b = [2, 3, 4]
* 比對兩個List 用 a 去跟 b 比較,回傳多的部分
* return [4] **/ public static <T> List<T> added(Collection<T> a, Collection<T> b) { return deleted(b, a); } /** * a = [1, 2, 3] b = [2, 3, 4]
* 比對兩個List 用 a 去跟 b 比較,回傳不同的部分
* return [1, 4] **/ public static <T> List<T> different(Collection<T> a, Collection<T> b){ List<T> list = new ArrayList<T>(); list.addAll(added(a, b)); list.addAll(deleted(a, b)); return list; } }

2017年4月21日 星期五

自定義Hibernate Type

原始問題:
某個老舊的table設定,其中一個欄位設char(10)
結果裡面的值有長有短,不足10碼的就自動補空白
導致在後端處理的時候處理很麻煩

當然最簡單的方式是改DB欄位設定
但總不是如人所願
所以只好針對Hibernate轉Bean的時候下手

先談失敗的改法:
我修改Hibernate塞Bean的時候一定要透過set方法 (找不到怎麼設的了QQ)
然後我在set方法中寫trim的動作
承原始問題,那些char設定很多都用在Primary Key
最後用Bean Update資料的時候就爆炸了
Exception在Hibernate這邊,還沒到DB
Exception的大意是Bean的Id被改過 (很久以前,也懶得還原當時情況了)

後來就改為現在這邊要介紹的方法

先自定義Hibernate Type
public class CustomerTrimStringType implements UserType{

    private int sqlType(){
        return Types.VARCHAR;
    }

    @Override
    public Object assemble(Serializable arg0, Object arg1) throws HibernateException {
        return null;
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public Serializable disassemble(Object arg0) throws HibernateException {
        return null;
    }

    @Override
    public boolean equals(Object value1, Object value2) throws HibernateException {
        if (value1 == null) {
            if (value2 != null) {
                return false;
            }
            return true;
        }

        return value1.equals(value2);
    }

    @Override
    public int hashCode(Object value) throws HibernateException {
        return value == null ? 0 : value.hashCode();
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
        throws HibernateException, SQLException
    {
        Object value = rs.getString(names[0]);
        if ((value == null) || (rs.wasNull())){
            return null;
        }

        return value.toString().trim();
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor arg3)
        throws HibernateException, SQLException
    {
        if(value == null){
            st.setNull(index, sqlType());
        }else{
            st.setString(index, (String)value);
        }
    }

    @Override
    public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
        return null;
    }

    @Override
    public Class returnedClass() {
        return String.class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{ Types.VARCHAR };
    }
}

在45行nullSafeGet這個方法做修改
另外,沒意外的話nullSafeSet這個方法是Hibernate -> DB的動作

設定Hibernate Bean的欄位要使用自定義的Type
public class TableBean {

    private String column1;

    @Column(name = "column1")
    @Type(type="packageName.CustomerTrimStringType")
    public String getColumn1() {
        return this.Column1;
    }

    public void setColumn1(String column1){
        this.column1 = column1;
    }
}

2017年4月20日 星期四

Struts Converter

原始問題:
Client端的時區是美國
Server端時區是台灣
當Client Submit資料時,Date就會有時區的差異沒辦法直接使用
所以配置了針對Date格式的轉換器去固定時區

1. 先建立轉換器的Class
public class DateConvert extends DefaultTypeConverter{
    @Override
    @SuppressWarnings("rawtypes")
    public Object convertValue(Map context, Object value, Class toType) {
        if(Date.class.equals(toType)){
            DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.TAIWAN);

            String[] str = (String[]) value;

            try {
                if(str != null && ! StringUtils.isBlank(str[0])){
                    return df.parse(str[0]);
                }else{
                    return null;
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        /*
        // 可以在同一個class針對不同的type做處理
        // 不過個人比較偏好一種寫一個class (有需求的話)
        else if(String.class.equals(toType)){
            do something
        }
        */
        return null;
    }
}

2. 建立struts2的配置文件xwork-conversion.properties,放在src路徑下
java.util.Date=pageName.DateConvert



參考連結:
http://www.blogjava.net/max/archive/2013/04/10/79602.html
http://www.cnblogs.com/lpshou/archive/2012/11/27/2791188.html

2017年4月19日 星期三

民國年選擇器 DatePicker

要搭配Jquery Datepicker

/**
 * Created by EIJI on 2014/1/3.
 */
(function(){
    var dateNative = new Date();

    // 補0函式
    var padLeft = function(str, len){

        if(str.length >= len){
            return str;
        }else{
            return padLeft(("0" + str), len);
        }
    };

    var funcColle = function(){
        this.onSelect = {
            basic: function(dateText, inst){
                /*
                var yearNative = inst.selectedYear < 1911 ? inst.selectedYear + 1911 : inst.selectedYear;
                */
                dateNative = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);

                // 年分小於100會被補成19**, 要做例外處理
                var yearTW;
                if (twSettings.yearPadZero) {
                    if(inst.selectedYear > 1911){
                        yearTW = padLeft((inst.selectedYear - 1911).toString(), 3);
                    }else{
                        yearTW = padLeft(inst.selectedYear.toString(), 3)
                    }
                }else{
                    if(inst.selectedYear > 1911){
                        yearTW = inst.selectedYear - 1911;
                    }else{
                        yearTW = inst.selectedYear;
                    }
                }
                var monthTW = padLeft((inst.selectedMonth + 1).toString(), 2);
                var dayTW = padLeft(inst.selectedDay, 2);

                return yearTW + twSettings.splitMark + monthTW + twSettings.splitMark + dayTW;
            }
        };
    };

    var twSettings = {
        closeText: '關閉',
        prevText: '上個月',
        nextText: '下個月',
        currentText: '今天',
        changeYear: true, //手動修改年
        changeMonth: true, //手動修改月
        yearRange: '1912:' + dateNative.getFullYear(),
        monthNames: [
            '一月','二月','三月','四月','五月',
            '六月','七月','八月','九月','十月',
            '十一月','十二月'
        ],
        monthNamesShort: [
            '一月','二月','三月','四月','五月',
            '六月','七月','八月','九月','十月',
            '十一月','十二月'
        ],
        dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'],
        dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'],
        dayNamesMin: ['日','一','二','三','四','五','六'],
        weekHeader: '周',
        dateFormat: 'yy/mm/dd',
        splitMark: '/', // 分割年月日的標誌
        firstDay: 1,
        isRTL: false,
        showMonthAfterYear: false,
        yearSuffix: '',
        yearPadZero: false,
        beforeShow:  function() {
            setTimeout(function(){
                $('.ui-datepicker').css('z-index', 1000);
            }, 0);
        }
        // ,defaultLastYear: false
        /*
            當沒有設defaultDate時,先用yearRange做defaultDate
            此時defaultLastYear設定預設為最後一年or第一年
        */
    };

    // 把yearText換成民國
    var replaceYearText = function(){
        var $yearText = $('.ui-datepicker-year');

        if(twSettings.changeYear !== true){
            $yearText.text('民國' + dateNative.getFullYear() - 1911);
        }else{
            // 下拉選單
/*
            if($yearText.prev('span.datepickerTW-yearPrefix').length === 0){
                $yearText.before("民國  ");
            }
*/
            $yearText.children().each(function(){
                if(parseInt($(this).text(), 10) > 1911){
                    $(this).text(parseInt($(this).text(), 10) - 1911);
                }
            });
        }
    };

    $.fn.datepickerTW = function(options){
        if(typeof options === "undefined"){
            options = {};
        }

        var fn = new funcColle();
        // setting on init,
        if(typeof options == 'object'){
            //onSelect例外處理, 避免覆蓋
            if(typeof options.onSelect == 'function'){
                fn.onSelect.newFunc = options.onSelect;
            }

            options.onSelect = function(dateText, inst){
                var outputValue = fn.onSelect.basic(dateText, inst);

                if(twSettings.yearPadZero){
                    outputValue = padLeft(outputValue, 9);
                }
                $(this).val(outputValue);

                if(typeof fn.onSelect.newFunc === 'function'){
                    fn.onSelect.newFunc(outputValue, inst);
                }
            };

            // year range正規化成西元, 小於1911的數字都會被當成民國年
            if(options.yearRange){
                var temp = options.yearRange.split(':');
                for(var i = 0; i < temp.length; i += 1){
                    //民國前處理
                    if(parseInt(temp[i], 10) < 1 ){
                        temp[i] = parseInt(temp[i], 10) + 1911;
                    }else{
                        if(parseInt(temp[i], 10) < 1911){
                            temp[i] = parseInt(temp[i], 10) + 1911;
                        }else{
                            temp[i] = temp[i];
                        }
                    }
                }
                options.yearRange = temp[0] + ':' + temp[1];
            }

            // 預設default Date
            if(options.defaultDate){

            }else if(options.yearRange){
                var temp = options.yearRange.split(':');

                if(options.defaultLastYear){
                    options.defaultDate = temp[1] - new Date().getFullYear() + 'y';
                }else{
                    options.defaultDate = temp[0] - new Date().getFullYear() + 'y';
                }
            }else if($.trim($(this).val()) != '' && $(this).val() != undefined){
                var tempDate = $(this).val().split(twSettings.splitMark);
                var tempYear = tempDate[0];

                var year;
                if(parseInt(tempYear, 10) < 1911){
                    year = padLeft((parseInt(tempYear, 10) + 1911).toString(), 4);
                }else{
                    year = parseInt(tempYear, 10);
                }

                options.defaultDate = year - new Date().getFullYear() + 'y';
            }
        }

        // setting after init
        if(arguments.length > 1){
            // 目前還沒想到正常的解法, 先用轉換成init setting obj的形式
            if(arguments[0] === 'option'){
                options = {};
                options[arguments[1]] = arguments[2];
            }
        }

        // override settings
        $.extend(twSettings, options);

        // init
        $(this).datepicker(twSettings);

        // beforeRender
        $(this).click(function(){
            var isFirstTime = ($(this).val() == '');
            // year range and default date
            if((twSettings.defaultDate || twSettings.yearRange) && isFirstTime){
/*              當有year range時, select初始化設成range的最末年
                已調整到上面"預設default Date"中
                if(twSettings.defaultDate){
                    $(this).datepicker('setDate', twSettings.defaultDate);
                }

                // 這段處理不好,因為「年」只改了外觀,但javascript實際值還是原本那年
                // 當有year range時, select初始化設成range的最末年
                if(twSettings.yearRange){
                    var $yearSelect = $('.ui-datepicker-year'),
                        nowYear = twSettings.defaultDate
                            ? $(this).datepicker('getDate').getFullYear()
                            : dateNative.getFullYear();

                    $yearSelect.children(':selected').removeAttr('selected');
                    if($yearSelect.children('[value=' + nowYear + ']').length > 0){
                        $yearSelect.children('[value=' + nowYear + ']').attr('selected', 'selected');
                    }else{
                        $yearSelect.children().last().attr('selected', 'selected');
                    }
                }
*/
            } else {
                var tempDate = $(this).val().split(twSettings.splitMark);

                if(tempDate.length != 3){
                    $(this).datepicker('setDate', new Date());
                }else{
                    var tempYear = tempDate[0];
                    var tempMonth = tempDate[1] - 1;
                    var tempDay = padLeft(tempDate[2], 2);

                    var year;
                    if(parseInt(tempYear, 10) < 1911){
                        year = padLeft((parseInt(tempYear, 10) + 1911).toString(), 4);
                    }else{
                        year = parseInt(tempYear, 10);
                    }

                    dateNative = new Date(year, tempMonth, tempDay);

                    $(this).datepicker('setDate', dateNative);
                }
            }

            var yearTW;
            if (twSettings.yearPadZero) {
                if(dateNative.getFullYear() > 1911){
                    yearTW = padLeft((dateNative.getFullYear() - 1911).toString(), 3);
                }else{
                    yearTW = padLeft(dateNative.getFullYear().toString(), 3);
                }
            }else{
                if(dateNative.getFullYear() > 1911){
                    yearTW = dateNative.getFullYear() - 1911;
                }else{
                    yearTW = dateNative.getFullYear();
                }
            }
            var monthTW = padLeft((dateNative.getMonth() + 1).toString(), 2);
            var dayTW = padLeft(dateNative.getDate().toString(), 2);

            $(this).val(yearTW + twSettings.splitMark + monthTW + twSettings.splitMark + dayTW);

            replaceYearText();

            if(isFirstTime){
                $(this).val('');
            }
        });

        // afterRender
        $(this).focus(function(){
            replaceYearText();
        });

        return this;
    };
})();

用法跟Jquery Datepicker一樣
    $element.datepickerTW();

在這邊有客製小功能:

    splitMark: '/' // 分割年月日的標誌
可自由設定年月日中間的分隔符號

    yearPadZero: false
年的部分(民國年)補滿3碼