2017년 11월 21일 화요일

qmail 큐(queue) 체크 스크립트.

계정의 패스워드가 노출되었거나, 사용자의 pc가 해킹 당했을 경우
해당 계정을 통해서 스팸 메일 발송이 자주 일어난다.

이렇게 되면 큐가 1000개는 기본으로 넘어가게 되어 정상적인 메일도
같이 안나게 된다.

정상적인 메일서버면 큐개수가 많아야 20~30개이므로
큐가 100개를 넘어가면 경고 메일을 보내도록 하였다.

아래 스크립트는 큐메일 용도이다.
#!/bin/sh
# Send an email when there are more then 1000 messages in the mail queue
# This is counted by the amount of lines in the qmail-qread output, so it's an indication...

show=$1
qread="/var/qmail/bin/qmail-qread"
qreadIDs=`/var/qmail/bin/qmail-qread | awk '{print $6}' | sed 's/#//' | grep -v '^$' | uniq`
len=`$qread | wc -l`
SUBJECT="WARNING: There are $len messages in the mail queue!----Country Check Version"
EMAIL="your@mail.com"
EMAILMESSAGE="/tmp/emailmessage.txt"
echo "" > $EMAILMESSAGE
chmod 777 $EMAILMESSAGE


if [ $len -gt 100 ]; then

for id in $qreadIDs
do
case "$id" in
"0.0.0.0"|"127.0.0.1"|"8.8.8.8")
continue;;
*)
#find /var/qmail/queue/mess/ -name 1449183 | xargs cat | grep mysolution-remoteip | awk '{print $2}' | xargs geoiplookup
queueFile=`find /var/qmail/queue/mess/ -name $id`
IP=`cat $queueFile | grep mysolution-remoteip | awk '{print $2}'`
# FROM=`cat $queueFile | grep From:`
# Subject=`cat $queueFile | grep Subject:`
countryChk=`geoiplookup $IP`
receiveChk=`cat $queueFile | grep Received:`

echo "Queue ID : $id --------------" >> $EMAILMESSAGE
echo "$IP : $countryChk" >> $EMAILMESSAGE
echo $FROM >> $EMAILMESSAGE
echo $Subject >> $EMAILMESSAGE
echo "$receiveChk" >> $EMAILMESSAGE
echo "-----------------------------" >> $EMAILMESSAGE
echo "" >> $EMAILMESSAGE

if [ 1 -eq $# ]; then

if [ $show = "show" ]; then
cat $EMAILMESSAGE
fi
fi
esac
done

MESSAGE=$(cat $EMAILMESSAGE)
# send an email using /bin/mail
#mail -s "$SUBJECT" "$EMAIL" < $EMAILMESSAGE
printf "To: ${EMAIL}\nSubject: ${SUBJECT}\n\n $MESSAGE" | /var/qmail/bin/qmail-inject
rm -rf $EMAILMESSAGE

fi

2017년 11월 16일 목요일

mybatis hashmap으로 값 가져오기.

한개의 필드의 값을 hashmap으로 가져오려고 한다.

DB 내용
key1:value1,key2:value2,key3:value3

List<String> 으로 가져온 것과 마찬가지로 typeHandler를 사용하여 처리하였다.

 

DTO
HashMap<String, Object> extraProperties;


public HashMap<String, Object> getExtraProperties() {
return extraProperties;
}

public void setExtraProperties(HashMap<String, Object> extraProperties) {
this.extraProperties = extraProperties;
}

 

mybatis resultmap
<result property="extraProperties" column="extraProperties" typeHandler="com.my.handler.StringSplitMapTypeHandler" />

 

handler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.my.util.CommonUtil;

public class StringSplitMapTypeHandler implements TypeHandler<HashMap<String,Object>> {

@Override
public void setParameter(PreparedStatement ps, int i, HashMap<String,Object> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public HashMap<String,Object> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");

}

@Override
public HashMap<String,Object> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}

@Override
public HashMap<String,Object> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
return CommonUtil.getStringSplitHashMap(columnValueStr, ",", ":");
}
}

 

getStringSplitHashMap 코드
	public static HashMap<String,Object> getStringSplitHashMap(String targetStr, String fieldSeparator, String mapSeparator){
if (targetStr == null) return null;
String[] fields = targetStr.split(fieldSeparator);
HashMap<String,Object> returnData = new HashMap<String,Object>();
for(String str : fields){
String[] splitStr = str.split(mapSeparator);
if(splitStr.length < 2) continue;
String key = splitStr[0];
String value = splitStr[1];
returnData.put(key, value);
}
return returnData;
}

mybatis List으로 값 가져오기.

DB 테이블 한개의 필드에 저장된 값을 구분자로 나눠서 list로 가져오려고 한다.

typeHandler를 써서 간단히 적용 완료.

DB 내용 searchTags 필드
value1, value2, value3

,를 구분자로 각각의 value들이 하나의 List 값이 된다.

DTO
List<String> searchTags;

public List<String> getSearchTags() {
return searchTags;
}
public void setSearchTags(List<String> searchTags) {
this.searchTags = searchTags;
}

 

mybatis resultmap
<result property="searchTags" column="searchTags" typeHandler="com.my.handler.SplitTypeHandler" />

 

StringSplitTypeHandler
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

public class SplitTypeHandler implements TypeHandler<List<String>> {

@Override
public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
if (parameter != null) {
ps.setString(i, parameter.toString());
}
}

@Override
public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
String columnValueStr = rs.getString(columnName);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValueStr = rs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}

@Override
public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValueStr = cs.getString(columnIndex);
if (columnValueStr != null) {
return Arrays.asList(columnValueStr.split(","));
}
return null;
}
}

 

 

 

2017년 11월 14일 화요일

자바 json 데이터 정렬(?) 예쁘게(?) 출력하기.

GSON 사용시.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(uglyJSONString);
String prettyJsonString = gson.toJson(je);

 

jettison 사용시
JSONObject json = new JSONObject(jsonString); // Convert text to object
System.out.println(json.toString(4)); // Print it with specified indentation

 

jackson 사용시
String test = "{\"age\":29,\"messages\":[\"msg 1\",\"msg 2\",\"msg 3\"],\"name\":\"mkyong\"}";
Object json = mapper.readValue(test, Object.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));

2017년 11월 3일 금요일

casperjs 팁

casperjs 로 대여섯개 사이트 자동 출첵을 걸어놨다.

삽질 중에 얻은 팁을 공유한다.

 

1. <input> 에 type 이 없을 경우 fill 이 안 먹힘. << 이런 개 같은 경우가 있음  그럴 때는 sendKey를 활용.

2. alert 한글이 깨지는 경우 setting 옵션에 encoding: "utf8", 를 넣으면 됨.

3. verbose: true로 해야 syntax 에러 나옴.

4.onsubmit="INVEN.Outlogin.login(this); return false;"   << 이런식으로 되어 있으면 fill true 가 제대로 동작 안함. click 이벤트로 처리 하도록

(INVEN이네..;;)

 

11번가 처리시
5. iframe 처리iframe접근은 casper.withFrame 를 사용.

 
==================
[debug] [phantom] opening url: http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, HTTP GET
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965, type=Other, willNavigate=true, isMainFrame=true << isMainFrame이 true
[debug] [phantom] url changed to "http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965"
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/html/blank.html, type=Other, willNavigate=true, isMainFrame=false << isMainFrame이 false임. iframe이란 소리.
[debug] [phantom] Navigation requested: url=http://www.11st.co.kr/jsp/event15/150625_benefitZoneAttend/iframe.jsp, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=about:blank, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Navigation requested: url=https://vars.hotjar.com/rcj-99d43ead6bdf30da8ed5ffcb4f17100c.html, type=Other, willNavigate=true, isMainFrame=false << iframe
[debug] [phantom] Successfully injected Casper client-side utilities
[info] [phantom] Step anonymous 5/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step anonymous 5/8: done in 6937ms.
[info] [phantom] Step _step 6/8 http://www.11st.co.kr/browsing/NewPlusZonePlace.tmall?method=getEventPage&addCtgrNo=951965 (HTTP 200)
[info] [phantom] Step _step 6/8: done in 6955ms.
==================

위의 로그를 보면 iframe이 4개임.
iframe 이름이 있으면 casper.withFrame('이름'... 이런식으로 주지만, 이름이 없으면 0부터 index를 준다.

11번가 자동 출첵, 2017.11.03 (casperjs)

기존에 curl 을 이용한 방식은 onclick등 자바스크립트 함수가 들어가면 매우 골치아파지는 문제가 있었다.

그래서 검색하다 'headless browser' 라는 걸 알게 됐고, casperjs 란 알게 됐다.


 

11번가 자동 출첵은 casperjs 로 만들어서 기존에 사용하고 있었는데,

9월 30일 이후로 출첵이 안된 거 보니 그 때 출석체크 페이지가 바뀐듯 하다.

 

11번가 자동 출첵은 왜 하느냐?

마일리지를 받기 위해서다. 마일리지로 뭘 할 수 있는냐?

사람들이 잘 모르는데, 11번가 마일리지로 할 수 있는게 많다.

상품쿠폰,배송비쿠폰, 핸드폰 데이터 쿠폰 그리고 상품 교환도 된다.

베스킨라빈스 레귤러 한번 먹어 봤다.

 

각설하고 11번가 자동 출체 코드는 아래와 같다.

var casper = require('casper').create({ pageSettings: { "userAgent": 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.10 (KHTML, like Gecko) Chrome/23.0.1262.0 Safari/537.10', "loadImages": true, "loadPlugins": false, "webSecurityEnabled": false, "ignoreSslErrors": true }, onWaitTimeout: function () { //throw new Error }, onStepTimeout: function () { //throw new Error }, encoding: "utf8", waitTimeout: 10000, stepTimeout: 10000, logLevel: "debug", // Only "info" level messages will be logged verbose: true // log messages will be printed out to the console }); var login_id = casper.cli.get("id"); var login_pw = casper.cli.get("pw"); var login_url = 'https://login.11st.co.kr/login/Login.tmall'; var attendance_url = 'http://www.11st.co.kr/browsing/MallPlanDetail.tmall?method=getMallPlanDetail&planDisplayNumber=935566'; //temp login_id = 'myid'; login_pw = 'mypass'; if(!login_id){ casper.echo("require id parameter"); // casper.exit(); //not working phantom.exit(); } if(!login_pw){ casper.echo("require pw parameter"); phantom.exit(1); } casper.start(login_url, function() { this.fill('form[name="login_form"]', { 'loginName' : login_id, 'passWord': login_pw }, false); this.click('#memLogin > div.save_idW > input'); this.wait(1000, function() { //this.echo("I've waited for a second."); }); }); //출석 casper.thenOpen(attendance_url, function(){ //iframe this.withFrame(1, function () { this.click('#regForm > div > div.sect03 > div.dev04 > a.get04 > img'); this.wait(1000, function() { this.setFilter("page.confirm", function(msg) { return true; }); }); }); }); //casper.run(); casper.run(function() { require('utils').dump(this.result.log); this.exit(); });

 

당연하지만 매일 자동으로 실행하려면 매일 켜져있는 pc(server)가 필요하다.

테스트용 리눅스 pc가 있어서 casperjs 설치 후 cron으로 매일 실행 되게끔 설정하였다.

 

없으신 분들은 알아서...;;

spring standalone application 에서 @service 실행

spring standalone application 에서 @service 실행

스프링 프로젝트에서 Main 함수안에서 기존에 구축된 @service 를 사용하려면

다음과 같이 하면 된다.
package xxx.xxx.www.test.console;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import xxx.xxx.www.test.service.TestService;

@Component
public class Main {

private static final String CONFIG_PATH = "classpath*:spring/all-config.xml";

public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_PATH);

Main p = context.getBean(Main.class);
p.start(args);
}

@Autowired
private TestService serv;
private void start(String[] args) throws Exception {
Map map = new HashMap();
map.put("docnum", "108");
List list = serv.selectList(map);
System.out.println("결과 logger:::::::::::::::: " + list);
}
}

 

한참 해맸었던 이유가 있는데, 기존에 스프링 환경은 아래와 같이 각 파트별로 구분지어 만들어진 환경이다.

spring-common.xml

spring-datasource.xml

....

이걸 부분만 적용하려니 잘 되지 않았다. 아래와 같이 한꺼번에 가져오도록 xml 파일을 하나 가져와서 해결.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

<import resource="classpath*:spring/spring-*.xml"/>
</beans>