2017年5月10日 星期三

紅豆粽子

材料:
紅豆 1 斤 (600公克)
6 兩 (250公克)
1 斤 (600公克)
糯米 2 斤 (1200公克)(1 斤約有10個粽子)

糯米泡半天水

準備餡料:
1. 將紅豆煮爛(約1.5小時)
2. 將紅豆加水放在漏盆中壓爛,下面用臉盆接
 目的有2點:a.去殼 b.取紅豆泥
示意圖:

3. 將第2步驟的紅豆泥水放到豆漿過濾袋脫水
4. 將紅豆、糖、豬油一起下鍋去攪拌
 油的話推薦使用豬油 (比較香) (另外豬油最好的是板油)
 吃素的話可以用植物油
(植物油的話要炒久一點,讓水分少一點比較好弄成一團一團的餡料)
 完成後約可以得到2斤的紅豆泥
5. 在以1球約55公克的紅豆泥捏成長條狀

6. 放冷凍
到這邊為止餡料告一段落

綁粽子:
1. 取兩片粽葉
 粽葉有兩面,一面為光滑面,一面為粗糙面(梗突起的為粗糙面)
 光滑面朝上
2. 將粽葉頭對尾重疊
圖一 圖二
3. 凹出容器的形狀(不要從中間凹,從1/4 or 3/4 處凹)
 看手順:
  凹右半邊要用第2點的圖一
  凹左半邊要用第2點的圖二

4. 塞米塞紅豆餡
5. 將粽葉閉合(待施工...)
6. 用繩子綁起來

大鍋水煮粽子約2小時

完成!!!

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