2012년 11월 20일 화요일

Javapns 2.2 버전에서 Push 보내기

Javapns 1.5 버전에서 개발하다가
"Client already exists" 에러가 자주 발생하게 되었다.
똑같은 Push를 애플 서버에 보내게 되면 이런 현상이 있구나~ 했는데..
운영으로 올려보니 그런 경우 이외에도 과부하 발생 시 계속 에러가 났다.

해결방법을 아무리 찾아봐도 찾질 못해서,
JAVAPNS 홈페이지에서 2.2로 최신 버전 업데이트를 하게 되었다 :(

2.2 library 를 올렸더니 이게 웬걸..
얼마나 업데이트 되었는지, 엄청난 에러가 발생했다.
영문으로 된 많은 설명서를 보면서 머리가  깨질 듯 아팠지만,
그나마 간단하게 정리된 wiki 페이지를 보고 이해할 수 있게 되었다.

참고 링크 : http://code.google.com/p/javapns/wiki/PushNotificationBasic 



# 다수의 디바이스, 각각의 전혀 다른 payload 그리고 Thread #

내가 하고자 하는 Push는
여러 디바이스에 해당하는 각각의 전혀 다른 메세지(payload)..
그리고 이걸 multi Thread를 사용해서 보내고자 했다.

위의 참고 링크를 보면 간단히 설명되어 있는 것 중,
다음의 방안을 찾아낼 수 있었다.

1) 사용 Payloads
payloads (keystore, password, production, numberOfThreads, payloadDevicePairs): use the built-in multithreaded transmission engine to push payloads to paired devices



2) 이중에서 payloadDevicePairs 변수가 궁금해서 찾아보니,

  • Object payloadDevicePairs: a list of paired of payloads and tokens to push. You can pass the following objects to this parameter:

    • javapns.notification.PayloadPerDevice[]: an array of PayloadPerDevice objects
    • java.util.List<PayloadPerDevice>: a list of PayloadPerDevice objects
    • javapns.notification.PayloadPerDevice: a single PayloadPerDevice object



3) 이렇단다..
이걸봐도 도무지 어떻게하면 "paired"해서 날릴 수 있는가 했더니,
정말 친절하게도 "자주 질문하는 내용" 으로 해결해 주었다. wow!

Frequently Asked Questions

  1. How to send a different payload for each device?: if each device should receive a different payload (even slightly different), you will need to create a PayloadPerDevice object for each device along with the payload it should receive. You can then use the Push.payloads(...)methods to push your payloads. 
  2. Example:
    List<PayloadPerDevice> payloadDevicePairs = new Vector<PayloadPerDevice>();
     
  3. <사용예제>
    payloadDevicePairs.add(new PayloadPerDevice(PushNotificationPayload.alert("Hello World 1!"), myDevice1)); 
    payloadDevicePairs.add(new PayloadPerDevice(PushNotificationPayload.alert("Hello World 2!"), myDevice2)); 
    payloadDevicePairs.add(new PayloadPerDevice(PushNotificationPayload.alert("Hello World 3!"), myDevice3)); 
    Push.payloads("keystore.p12", "keystore_password", false, payloadDevicePairs);


이제 개발할 수 있겠다..!!

2012년 8월 28일 화요일

[jsp] 에러페이지 web.xml 로 예외처리하기 (404, 505, nullpointer)

web.xml<error-page>
  <error-code>404</error-code>   //에러코드
  <location>/error.404.jsp</location>   //에러 페이지의 URI
</error-page>

<error-page>
  <error-code>500</error-code>   //에러코드
  <location>/error.500.jsp</location>   //에러 페이지의 URI
</error-page>

<error-page>
  <exception-type>java.lang.NullPointerException</exception-type>   //에러코드
  <location>/errorNullPointer.jsp</location>   //에러 페이지의 URI
</error-page>


404에러.jsp<%
response.setStatus(HttpServletResponse.SC_OK)
%>

<body>
요청한 페이지는 존재하지 않습니다!!

에러타입:: <%= exception.getClass().getName()%>  //익셉션 기본 객체의 클래스 이름을 출력
에러메세지:: <%= exception.getMessage()%>   //예외메세지출력
</body>

500에러.jsp

<%
response.setStatus(HttpServletResponse.SC_OK)
%>

<body>
빠른시간안에 문제를 해결하도록 하겠습니다.
에러타입:: <%= exception.getClass().getName()%>  //익셉션 기본 객체의 클래스 이름을 출력
에러메세지:: <%= exception.getMessage()%>   //예외메세지출력
</body>


nullpointer.jsp

<%
response.setStatus(HttpServletResponse.SC_OK)
%>

<body>
서비스과정에서 null예외가 발생하였습니다.
</body>




#<% response.setStatus(HttpServletResponse.SC_OK) %>

:응답 코드를 200으로 지정한다. 이렇게 응답코드를 200으로 지정하지 않으면 웹프라우저에는 404 응답 코드가 전달되며,
이경우 웹프라우저는 자체적으로 404에러일때 보여주는 화면을 출력한다.
따라서 error404.jsp의 출력결과가 웹프라우저에 그대로 출력되도록 하기 위해서는 응답코드를 200으로 지정해주어야 한다
이경우 톰캣의 경우에 해당하며 사용하는 웹콘테이너에 따라서 이렇게 안해줘도 원하는 결과가 출력되기도 한다.





-------------------------------------------------------------------------------
자세하게 보기



JSP 페이지의 에러 처리 - 이젠 에러 페이지를 내 손으로 만들어보자!

[] 에러페이지를 사용한 에러 처리 예제

1. 먼저 에러가 발생되는 페이지인 readParameter.jsp 페이지를 코딩하고 저장한다.





 - 2번 줄에 <% page errorPage = "/error/viewErrorMessage.jsp" %> 에 에러가 발생하면 에러 페이지를 보여줄 경로를 적어준다.
   경로를 정확히 하지 않으면 내가 만든 errorPage가 아닌 원래 errorPage가 나올 것이다.
- 그럼 10번 줄에서 일부러 오타를 내보고 실행을 시켜보자. toUpperCase 이렇게 뒤에 괄호를 빼고 실행을 시켜보자.


2. 에러페이지인 viewErrorMessage.jsp 를 /error/폴더 밑에다가 작성하도록 하자.





3. 웹 브라우저를 실행시켜 결과를 확인해보자.




막상 실행을 시켜보면 에러 페이지에 의한 에러 처리가 일어나지 못함을 볼 수 있다.
톰캣의 버전에 따라 결과가 다를 수 있다. 이것은 컨테이너에 따라 에러 페이지에 의한 에러 처리를 지원하지 않을 수 있기 때문이다.

실제로 현재의 에러 처리는 에러 페이지를 사용하지 않고 에러 코드별 처리나 에러 종류별 처리를 사용한다.
에러가 한 가지 종류만 발생하는 것이 아니기 때문이다. 이번엔 에러 코드별 처리에 대해 알아보자.

- 우리가 가장 많이 사용하는 웹브라우저 에러 코드
404 : Not Fount, 문서를 찾을 수 없음. 이 에러는 클라이언트가 요청한 문서를 찾지 못한 경우에 발생. (URL 제대로 입력 되었는지 확인)
500 : Internal Server Error. 이 에러는 웹 서버가 요청사항을 수행할 수 없을 경우에 발생. (대부분이 코딩 에러)



에러 코드별 처리를 어떻게 해야 할까? 답은 web.xml 이다.

[] 에러 코드별 에러 처리 예제

1. 먼저 에러를 코드별로 처리하는 web.xml에 작성한다.
<error-page>
<error-code>에러 코드</error-code>
<location>에러 페이지의 URI</location>
</error-page>



편의상, 500 에러가 발생했을 때 요청되는 페이지와 404 에러가 발생했을 때 요청되는 페이지를 같은 곳으로 처리했다.


2. 에러 페이지인 viewErrorMessage.jsp 페이지를 작성한다.



 -  response.setStatus(HttpServletResponse.SC_OK);
    이 코드는 현재 페이지가 정상적으로 응답되는 페이지임을 지정하는 코드이다.
    이렇게 지정하지 않으면 웹 브라우저에는 404 응답코드가 전달되며, 이 경우 웹브라우저는 자체적으로 404 에러일 때 보여주는 화면을 출력한다.
    따라서 우리가 지정한 에러 메세지의 출력 결과가 웹 브라우저에 그대로 출력되도록 하기 위해서는 이렇게 지정해주어야 한다.



일부러 에러를 발생시켜서 실행시키면, 우리가 지정한 에러 메세지가 출력되는 것을 확인할 수 있다.

똑똑한 미나의 에러처리방법 ^__________^ V




이왕 에러페이지 시작한 김에 예외 페이지까지 함께 살펴보자.

[] 예외 종류별 에러 페이지 지정하기.

1. web.xml 작성
JSP 페이지에서 발생하는 예외 종류에 따라서 에러 페이지를 지정할 수 있다.
앞에서 살펴봤던 에러 코드별 에러 페이지 지정 방법과 거의 같은 방법으로 지정할 수 있는데,
다음과 같이 <error-code>태그 대신에 <exception-type> 태그를 사용하면 된다.





2. 에러를 발생시킬 코드 readParameter2.jsp 작성





3. /error/errorNullPointer.jsp 작성






이 페이지는 앞서 작성한 readParameter.jsp 와 동일하며 에러페이지만 지정하지 않았다. 즉 readParameter.jsp의 2번째 줄이 없다.
따라서 readParameter2.jsp를 실행할 때 name 파라미터를 전달하지 않으면 NullPointerException이 발생하게 되는데,
이 때 web.xml 파일에 이 예외에 대한 에러 페이지를 앞에서처럼 지정했다면 다음과 같은 결과가 출력 될 것이다.








지금까지 에러 페이지를 지정하는 3가지 방법에 대해서 살펴봤었는데, 에러 페이지를 선택할 때에는 다음과 같은 우선 순위를 따르게 된다.

1. Page 디렉티브의 errorPage 속성에서 지정한 에러 페이지를 보여준다.
2. JSP 페이지에서 발생한 예외 타입이 web.xml 파일의 <exception-type>에서 지정한 예외 타입과 동일한 경우 지정한 에러 페이지를 보여준다.
3. JSP 페이지에서 발생한 에러 코드가 web.xml파일의 <error=code>에서 지정한 에러 코드와 동일한 경우 지정한 에러 페이지를 보여준다.
4. 아무것도 해당되지 않을 경우, 톰캣 엔진이 제공하는 기본 에러 페이지를 보여준다.

[출처] JSP 에러페이지 처리|작성자 Arari

2012년 8월 8일 수요일

JNDI로 DB2 연결하기 (환경설정 Tomcat7.0+DB2+Eclilpse)

1. xml 환경설정

(1) context.xml (프로젝트>Webapp>META-INF>context.xml)
------------------------------------------------------------------------------

<Context>

    중략 ... ...

<Resource name="jdbc/techLabsDS"
 auth="Container"
 type="javax.sql.DataSource"
 username="아이디입력"
 password="패스워드입력"
 driverClassName="com.ibm.db2.jcc.DB2Driver"
 url="jdbc:db2://연결할 해당 IP주소 입력:50000/DB명 입력"
 maxActive="8"
 maxIdle="4" />

</Context>
------------------------------------------------------------------------------


(2) server.xml (프로젝트>Webapp>META-INF>server.xml)
------------------------------------------------------------------------------

<GlobalNamingResources>
 
   중략 ... ...
             
              <Resource name="jdbc/techLabsDS" auth="Container"
type="javax.sql.DataSource"
username="아이디입력"
password="패스워드입력"
driverClassName="com.ibm.db2.jcc.DB2Driver"
url="jdbc:db2:// 연결할 해당 IP주소 입력 :50000/ DB명 입력 "
maxActive="8"
maxIdle="4"/ >


  </GlobalNamingResources>
------------------------------------------------------------------------------



(3) web.xml (프로젝트>Webapp>WEB-INF>web.xml)
------------------------------------------------------------------------------

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
metadata-complete="true" version="3.0">

<resource-ref>
<description>DB Connection Pool </description>
<res-ref-name>jdbc/techLabsDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>



</web-app>
------------------------------------------------------------------------------


(4) 위 1, 2, 3 에서 주의사항!!

  - url 입력 시 localhost 보다는 IP주소를 입력하는 것이 잘 연결된다. (내 경우에는 무조건 에러 발생)
  - Resource name 이나 res-ref-name dp 에 있는 명칭은 반드시 똑같이 한다. (이걸로 연결하기 때문에..)



(5) jsp 작성
------------------------------------------------------------------------------

<%@ page import="java.sql.*, javax.naming.*,
javax.naming.Context, javax.naming.InitialContext, javax.sql.DataSource" %>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">

<title>Insert title here</title>
</head>
<body>
테스트

<%
Statement stmt = null;
ResultSet rs = null;
Connection con = null;
try{
Context initCtx = new InitialContext();
Context envCtx = (Context)initCtx.lookup("java:comp/env/");
DataSource ds = (DataSource)envCtx.lookup("jdbc/techLabsDS");
con = ds.getConnection();
out.write("DBCP Connection. <br><br>");
String sQuery = "sql 구문 입력";
stmt = con.createStatement();
rs = stmt.executeQuery(sQuery);
while (rs.next()) {
   out.write(rs.getString(1) + "\t");
   }
}catch (Exception ex) {
ex.printStackTrace();
} finally {
if (rs != null) try {rs.close();} catch (Exception ex2) {}
if (stmt != null) try {rs.close();} catch (Exception ex3) {}
if (con != null) try {rs.close();} catch (Exception ex4) {}
}
%>
</body>
</html>
------------------------------------------------------------------------------
위 색칠된 부분이 연결 부분임
(굵은 글씨는 에러가 가장 많이 발생되는 부분으로, 에러 발생 시 이 부분을 중점적으로 수정하면 된다.)




2012년 7월 27일 금요일

JAVA 매개변수 메소드 사용법


public class MyDate{

private int day = 1;
private int month = 1;
private int year = 2000;

public MyDate(){
}

public MyDate(int day, int month, int year){
this.day = day;
this.month = month;
this.year = year;
}

public MyDate(MyDate Date){
this.day = Date.day;
this.month = Date.month;
this.year = Date.year;
}

public MyDate addDays(int more_days){
MyDate new_date = new MyDate(this);
new_date.day = new_date.day + more_days;
return new_date;
}

public void print(){
System.out.println("MyDate: " + Date + "-" + month + "-" + year);
}

}

public class TestMyDate {
public static void main(String[] args){
MyDate init_birth = new MyDate();
MyDate my_birth = new MyDate(22, 8, 1971);

init_birth.print();
my_birth.print();

MyDate next_week = my_birth.addDays(7);
next_week.print();
}
}

2012년 6월 25일 월요일

안드로이드 EditText 주민등록번호 뒤6자리 자동 * 입력



String strCollectAmount1="";//보험료addComma에 사용되는 data
String strCollectAmount2="";//주민번호addComma에 사용되는 data



I_CARD_OWN_CD2.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
System.out.println("s>>>>>>>>>>>>>>"+s);
if(!s.toString().equals(strCollectAmount2)){
if(s.length() > 0){ //주민번호 m_temp_OwnNum[1]에 입력
if(s.length() == m_temp_OwnNum[1].length()+1){
m_temp_OwnNum[1] += s.charAt(s.length() -1); 
System.out.println("하나씩"+m_temp_OwnNum[1]);
}else{
m_temp_OwnNum[1] = s.toString();
System.out.println("한번에 "+m_temp_OwnNum[1]);
}
}else{
m_temp_OwnNum[1] = "";
}
strCollectAmount2 = Util.juminChange(s.toString());//뒷자리 임시 (*들어감)
I_CARD_OWN_CD2.setText(strCollectAmount2);
Editable e = I_CARD_OWN_CD2.getText();
Selection.setSelection(e, strCollectAmount2.length());
}
if(I_CARD_OWN_CD2.length()==7){
Data.setI_OWNER_NO(m_temp_OwnNum[0]+m_temp_OwnNum[1]); // 주민번호 저장
System.out.println("setI_OWNER_NO>>>>>>>>>>>>>>"+Data.getI_OWNER_NO());
}
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {}

@Override
public void afterTextChanged(Editable s) {}
});







/**
* 주민번호 뒷자리 '*' 표 처리

* @param value
* @return
*/
public static String juminChange(String value) {
String juminBefore_value = value;
String juminAfter_value = "";


if (juminBefore_value.length() <= 0)
return "";


for (int i = 0; i < juminBefore_value.length(); i++) {
char temp = juminBefore_value.charAt(i);


if (i >= 1) {
temp = '*';
}


//System.out.println("chart(" + i + ") ----> " + temp);


juminAfter_value += temp;
}


return juminAfter_value;
}

안드로이드 EditText 입력 할 때마다, 콤마 자동 추가


String strCollectAmount ="";   // --> 입력할때마다 문자가 채워지므로 첨엔 "" 해도 된다.

// EditText 입력 할 때마다, 콤마 자동 추가
final EditText f230_REALMONEY_TD = (EditText) findViewById(R.id.f230_REALMONEY_TD);
f230_REALMONEY_TD.setInputType(InputType.TYPE_CLASS_NUMBER);


f230_REALMONEY_TD.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}


public void onTextChanged(CharSequence s, int start, int before,
int count) {
// 숫자가 추가되었을때에 Comma를 추가해준다.
if (!s.toString().equals(strCollectAmount)) {
// 숫자에 Comma를 추가해주는 메소드 호출
strCollectAmount = Util.makeStringWithComma(s.toString()
.replace(",", ""), true);
f230_REALMONEY_TD.setText(strCollectAmount);
Editable e = f230_REALMONEY_TD.getText();
// 커서의 위치가 현재 입력된 위치의 끝쪽에 가게 해야 한다.
Selection.setSelection(e, strCollectAmount.length());
}
}


public void afterTextChanged(Editable s) {
}
});


/**
* 문자열에 통화적용을 위해 컴마를 표기한다.

* @param string
*            통화적용을 위한 문자열
* @param ignoreZero
*            값이 0일 경우 공백을 리턴한다.
* @return 통화적용이 된 문자열
*/
public static String makeStringWithComma(String string, boolean ignoreZero) {
if (string.length() == 0) {
return "";
}
try {
if (string.indexOf(".") >= 0) {
double value = Double.parseDouble(string);
if (ignoreZero && value == 0) {
return "";
}
DecimalFormat format = new DecimalFormat("###,##0.00");
return format.format(value);
} else {
long value = Long.parseLong(string);
if (ignoreZero && value == 0) {
return "";
}
DecimalFormat format = new DecimalFormat("###,###");
return format.format(value);
}
} catch (Exception e) {
e.printStackTrace();
}
return string;
}

2012년 6월 22일 금요일

안드로이드 StartForActivityResult 쓰는 방법


[Activity A 사이드]
private static final int B_ACTIVITY = 0;

public void onCreate(){
...
Intent a_i = new Intent(this, B.class);
startActivityForResult(a_i, B_ACTIVITY);
...}

public void onActivityResult(int requestCode, int resultCode, Intent intent){
super.onActivityResult(requestCode, resultCode, intent);

switch(requestCode){
case B_ACTIVITY: // requestCode가 B_ACTIVITY인 케이스
if(resultCode == RESULT_OK){ //B_ACTIVITY에서 넘겨진 resultCode가 OK일때만 실행
intent.getExtras.getInt("data"); //등과 같이 사용할 수 있는데, 여기서 getXXX()안에 들어있는 파라메터는 꾸러미 속 데이터의 이름표라고 보면된다.
}
}
}

[Activity B 사이드]

Bundle extra;
Intent intent;

onCreate(){
...
extra = new Bundle();
intent = new Intent(); //초기화 깜빡 했다간 NullPointerException이라는 짜증나는 놈이랑 대면하게 된다.
...

extra.putInt("data", 1);
intent.putExtras(extra);
this.setResult(RESULT_OK, intent); // 성공했다는 결과값을 보내면서 데이터 꾸러미를 지고 있는 intent를 함께 전달한다.
this.finish();
}