'java'에 해당되는 글 28건

  1. 2011.11.23 xpath 를 이용, java 에서 xml 문서 쉽게 파싱하기 5
  2. 2011.11.17 이클립스(eclipse) 실행시 Java was started but returned exit code=13 에러가 날때 4
  3. 2011.11.16 제우스 + 스프링 프로젝트 디플로이시 java.lang.IllegalStateException: Unable to locate the default servlet for serving static content 이 발생할때 4
  4. 2011.11.16 재귀함수(Recursive funtion)을 이용한 특정 폴더 아래의 모든 파일정보 읽어서 출력하기
  5. 2011.11.16 JAVA/JSP SQL Injection 해킹 방지를 위한 코딩 5

xpath 를 이용, java 에서 xml 문서 쉽게 파싱하기

xpath 를 이용할수 있다는 걸 알기전에는 무식하게도 Document 클래스의 getElementById() 나 getElementsByTagName() 메소드를 이용해서 

상당히 무식하게 난해한 코드로 xml 을 파싱하곤 했었다 -_-



어느날 문득 xpath 에 대해서 살짝 알게되고 나서 부터는 전에 쓰던 방법에 비해서는 아주 간결하고 이해하기 쉬운 코드로 xml 에서 원하는 데이터를 땡겨다 쓰는게 훨씬 수월해 졌다.




※ xpath 문법에 대해서 왠만한걸 쉽게 파악하기 위해서는 요 사이트에 가면 된다.

http://www.zvon.org/xxl/XPathTutorial/General/examples.html

요기 가서 보면 알겠지만 쉽게 쉽게 설명되있다.

xpath 만 어느정도 쓸 수 있으면 xml 에서 원하는 데이터를 땡겨오는건 매우 쉬워지니 살짝 한번 연구해 보는걸 추천한다.





xpath를 이용해서 xml 에서 원하는 데이터를 파싱하는 예제코드를 작성해 보았다.

xpath 를 사용하기 위해서 별다로 다른 라이브러리를 classpath 에 추가 안해도 된다.




예제 코드에서 사용된 xml
<root>
	<row>
		<col1 id="c1">값1</col1>
		<col2 id="c2" val="val2">값2</col2>
	</row>
	<row>
		<col1 id="c3">값3</col1>
		<col2 id="c4">값4</col2>
	</row>
</root>



예제 코드는 다음과 같다.
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;


public class XPathTest{
	public static void main(String[] args) throws Exception {
		String xml = "<root><row><col1 id='c1'>값1</col1><col2 id='c2' val='val2'>값2</col2></row>" + 
                             "<row><col1 id='c3'>값3</col1><col2 id='c4'>값4</col2></row></root>";
		
		// XML Document 객체 생성
		InputSource is = new InputSource(new StringReader(xml));
		Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);

		// 인터넷 상의 XML 문서는 요렇게 생성하면 편리함.
		//Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
		//                               .parse("http://www.example.com/test.xml");
		
		
		
		// xpath 생성
		XPath xpath = XPathFactory.newInstance().newXPath();
		

		
		
		// NodeList 가져오기 : row 아래에 있는 모든 col1 을 선택
		NodeList cols = (NodeList)xpath.evaluate("//row/col1", document, XPathConstants.NODESET);
		for( int idx=0; idx<cols.getLength(); idx++ ){
			System.out.println(cols.item(idx).getTextContent());
		}
		// 값1   값3  이 출력됨
		
		
		
		
		
		// id 가 c2 인 Node의 val attribute 값 가져오기
		Node col2 = (Node)xpath.evaluate("//*[@id='c2']", document, XPathConstants.NODE);
		System.out.println(col2.getAttributes().getNamedItem("val").getTextContent());
		// val2 출력
		
		
		
		
		
		// id 가 c3 인 Node 의 value 값 가져오기
		System.out.println(xpath.evaluate("//*[@id='c3']", document, XPathConstants.STRING));
		// 값3 출력
	}
}


전에 getElementById(), getElementsByTagName() 요걸 쓰던 코드에 비하면 코드도 쉽게 읽히고 심플하기도 서울역에 그지없다.

앞으로 xml 파싱할때는 xpath 를 주로 써야겠다.



※ evalueate() 메소드 맨 끝에 들어가는 파라메터로

XPathConstants.NODESET
XPathConstants.NODE
XPathConstants.BOOLEAN
XPathConstants.NUMBER


이클립스(eclipse) 실행시 Java was started but returned exit code=13 에러가 날때

램을 4기가로 업그레이드 하면서 4기가 램을 모두 활용하기 위함과 동시에 깔쌈하고 쾌적한 윈도우로 다시 돌아가기 위해 64bit 윈도우7을 설치했다.

다 설치한다음에 쓸 후로그램들을 이것저것 다 설치하고

흥미진진한 개발의 세계로 발을 다시 살포시 디밀기 위해 JDK 도 64bit 버전으로 뿟 설치했다.




이클립스도 새로 받아서 하면 좋겠지만 훌러그인들을 다시 설치하는게 시간도 많이 걸리고 귀찮기도 해서 전에 쓰던 이클립을 그대로 쓰기로 했다.
(※ 이클립은 포맷하기 전에 압축해서 백업해 뒀다가 다시 압축만 풀어주면 전에 쓰던 이클립을 고대로 쓸 수 있다.)

이클립을 실행하려고 따블클릭을 했는데 실행이 안된다. Java was started but returned exit code=13 에러와 함께.


 왜 그런가 했더만, 이유는 JDK는 64bit 버전을 설치했는데 이클립은 32bit용 버전이였기 때문이다.

64bit용 이클립을 새로 받아서 압축풀고 실행을 했더니 실행이 잘된다. 다시 훌러그인들을 설치해야 하지만 -_-;



아무튼 Java was started but returned exit code=13  에러가 뜰때는 JDK 에 맞는 이클립 32bit용 혹은 64bit용을 새로 받아야 한다~~~ 


제우스 + 스프링 프로젝트 디플로이시 java.lang.IllegalStateException: Unable to locate the default servlet for serving static content 이 발생할때


개발환경으로 Jetty 를 써가며 개발하다 실제 서버인 JEUS 에 프로젝트를 디플로이 할려고 했는데

java.lang.IllegalStateException: Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.

어쩌고 하는 에러가 났다.




쪽~~~ 찾아보니 에러가 나는 이유는 "/" 경로를 처리하기 위해 등록했던 스프링 설정파일 중의 <mvc:default-servlet-handler/> 요 설정 때문이였다.




원래 <mvc:default-servlet-handler/> 설정할때 <mvc:default-servlet-handler default-servlet-name="디폴트서블릿 이름"/> 요런식으로 WAS 의 디폴트 서블릿 이름을 설정해 줘야 하는데,

tomcat, resin, weblogic, websphere 같은 외국에서 맨든 was 들은 <mvc:default-servlet-handler/> 요렇게 편리하게 설정할수 있도록

스프링 개발자가 주요 was 에 대해 처리를 해줘서, 굳이 디폴트 서블릿 이름을 안적어 줘도 됬었던 것이다.




아무튼 Jeus 에서 에러를 해결하기 위해서는 Jeus 에서 쓰는 디폴트 서블릿 이름인 WorkerServlet 을
<mvc:default-servlet-handler default-servlet-name="WorkerServlet"/>

요래 설정해 주면 해결할 수 있다.





※ 디폴트 서블릿 이름을 알려면 ?
각 was 설치 디렉토리 아래쪽으로 설정 파일들 모아 놓은데 보면 web.xml 파일이 있다. 예를 들어 톰캣 같은 경우는 톰캣설치디렉토리/conf/web.xml 파일이 되겠다.

이 파일을 열어보면
<servlet>
	<servlet-name>default</servlet-name>
	<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
	<init-param>
		<param-name>debug</param-name>
		<param-value>0</param-value>
	</init-param>
	<init-param>
		<param-name>listings</param-name>
		<param-value>false</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>
요렇게 되 있는데 <servlet-name>default</servlet-name> 에서 default 가 디폴트 서블릿 이름이다.





※ was 별 default servlet name
Tomcat : default
Resin : resin-file
Weblogic :  FileServlet
WebSphere : SimpleFileServlet
jetty : default
jboss : default
jeus : WorkerServlet



재귀함수(Recursive funtion)을 이용한 특정 폴더 아래의 모든 파일정보 읽어서 출력하기


왠지 도움이 될것 같다면 추천을 *(-_-)*


이런건 학교 뎅길때 숙제로 많이 하던것이다.

복잡해 보이는 문제도 이빠이 작은 부분으로 쪼개서 생각하고 고걸 recursive funtion 화 시키면 쉽게! 심플하게! 문제를 해결할 수 있다.



요 문제의 경우, 작게 쪼개서 생각할만한 부분은

어떤 디렉토리가 있으면 그 디렉토리 안에 있는 디렉토리들과 파일들을 출력할래염!!! 이다.

디렉토리의 디렉토리 안에 있는 또 다른디렉토리와 또 그 많은 파일을의 출력에 대해서는 생각하지 않는다.

어떤 디렉토리가 있으면 그 디렉토리 안에 있는 디렉토리들과 파일들을 출력할래염!!!에 대해서 recursive function 을 맹글면 된다.



어렴풋이 생각나는 학창시절의 배움을 더듬어 보면 그냥 쌩으로 for 문을 써서 하는게 속도면에서는 훨씬 빠르다.(그런데 코드는 훨씬 복잡해질것이다.)

하지만 요즘 컴퓨터는 그때의 컴퓨터가 아니다. 나는 컴퓨터를 믿는다. *(-_-)*



성능이 매우매우매우매우매우 중요할 때에는 recursive function 을 쓰면 안되겠지만 그럴 경우가 아니면야 가독성 있는 코딩을 할 수 있는 방법이 훨씬 좋다고 생각한다.




잡소리는 고만하고, source!
package com.tistory.stove99;

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FileListExporter {
	private StringBuffer sb = new StringBuffer();
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

	String targetDir; // 리스팅할 대상 디렉토리
	File outputFile;  // 결과를 출력할 파일

	public FileListExporter(String targetDir, File outputFile) {
		this.targetDir = targetDir;
		this.outputFile = outputFile;
	}

	public void export() {
		// StringBuffer 로 파일정보 싹 읽어 들이기
		traversDir(new File(targetDir), 0);

		// 파일로 출력
		PrintWriter pw = null;
		try {
			pw = new PrintWriter(outputFile);
			pw.append(sb);
			pw.flush();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			if (pw != null)
				pw.close();
		}
	}

	// recursive funtion
	private void traversDir(File dir, final int level) {
		dir.listFiles(new FileFilter() {

			public boolean accept(File file) {
				String modified = sdf.format(new Date(file.lastModified()));

				// 탭처리 해서 트리처럼 보이게 하기
				for (int idx = 0; idx < level; idx++) sb.append("\t");

				if (file.isDirectory()) {
					sb.append(String.format("%-35s <dir> %s", file.getName(), modified))
						.append("\r\n");
					traversDir(file, level + 1);
				} else {
					sb.append(String.format("%-35s %d %s", file.getName(), file.length(), modified))
						.append("\r\n");
				}

				return false;
			}

		});
	}

	// test
	public static void main(String[] args) {
		new FileListExporter("C:\\Program Files", new File("c:\\list.txt")).export();
		System.out.println("export end");
	}
}


파일 출력하는것 때문에 코드가 약간 길어지긴 했는데 딱 보면 코드가 그렇게 길지도 않다. 요런 짧으면서도 이해가 뽓 잘되는 코드를 맹글수 있다는게 recursive funtion의 매력인것 같다 *(-_-)*

요걸 뽓 실행해 보면 C 드라이브에 요런 list.txt 파일이 생성된다.


 

JAVA/JSP SQL Injection 해킹 방지를 위한 코딩


SQL Injection 은 사용자가 요상한 문자열을 입력해서 쿼리를 요상하게 바꿔 버려서 아무 아이디로나 로그인이 훅 되버린다던지 하는 쪼금은 싱거운 해킹방법중의 하나라고 나는 알고 있다.


뭐 예를들어 아이디 비번을 입력하고 로그인 할때

아이디로  
admin' OR '1'='1 요런걸 입력하고 로그인을 했을때 admin 으로 싹 로그인 되 버리는 어처구니 없는 일이 발생하는 것이 SQL Injection 처리를 쪽바로 해주지 않았을때 일어나는 참혹한 일이다.


SQL Injection 이 발생하는 전형적인 코드는 다음과 같다.
String userid=request.getParameter("userid");
String password=request.getParameter("password");

....
....

Statement stmt = conn.createStatement();
ResultSet rs = 
   stmt.executeQuery("select count(*) as cnt from member where userid='"+userid+"' and password='"+password+"'");

....
....



위 코드처럼 사용자 정보를 받아서 쿼리를 실행하고 실행결과가 1 이면 로그인 시켜주는 코드가 있다.

그런데 사용자가 아디디로 admin' OR '1'='1  을 입력하고 비밀번호로 abcd 를 입력한 후 로그인 시켜주셈~ 이라고 하면 우째될까?

위 코드대로라면  select count(*) from member where userid='amdin' OR '1'='1' and password = 'abcd'   이 쿼리가 실행되 버린다 -_-;;;

무섭다. 만약 사용자 테이블에 userid 가 admin 이라는 레코드가 있다면 admin 으로 로그인 되버리는 것이다. 

이런 어처구니 없는일이 놀랍게도 몇몇 허접한 사이트에서는 더러 통하는데도 있다 . -_-



아무튼 헛소리는 고만하고 본론으로 들어가서 SQL Injection 을 방지하려면 우째해야 될까? 

Statement 대신  PreparedStatement 를 쓰면 된다. 요렇게.
....
....

PreparedStatement stmt = conn.prepareStatement("select count(*) from member where userid=? and password=?");
stmt.setString(1, userid);
stmt.setString(2, password);

ResultSet rs = stmt.executeQuery();

....
....



PreparedStatement 만 쓰면 해결된다고 해서 또 요렇게는 쓰지 말길 -.-;
PreparedStatement stmt = 
   conn.prepareStatement("select count(*) from member where userid='" + userid + "'" and password='" + password + "'");



※ 하이버네이트는 한번도 안써봐서 잘 모르겠는데 mybatis 나 ibatis 같은 경우는 무난하게 #{xxxx} 요런것만 썼다면 SQL Injection 이 방지된다.

그런데 WHERE 절에 ${xxxx} 이렇게 있다면 다시 한번 생각해 보기 바란다.

${xxxx} 이거는 문자열 더하는것과 똑같기 때문에 위험하다. ${xxxx} 요런건 ORDER BY 절 같은데만 사용하자.


prev 1 2 3 4 5 6 next