'개발 이야기/유용한 Coding'에 해당되는 글 26건

  1. 2014.08.18 I/O Docs 구조
  2. 2014.08.18 I/O Docs 설치하기
  3. 2010.06.03 java로 excel 다중 sheet 만들기
  4. 2009.08.25 jdbc 사용 예제 1
  5. 2009.02.02 Struts2 + Spring2.0 엮을 때 나는 NullPointerException 해결방법 1
  6. 2008.12.01 FTP 파일 업로드
  7. 2008.11.30 임베디드 WAS인 Jetty 사용하기
  8. 2008.11.25 Ant로 배포했는데 막상 배포는 안되고 loader 폴더만 남아있을 땐?
  9. 2008.11.17 Java로 쉽게 메일 보내기 메소드 만들기
  10. 2008.11.17 Collections.sort 로 쉽게 소트하기
  11. 2008.07.29 Velocity에서 tiles 파일 불러오는 거 안될 때
  12. 2008.07.28 ANT FTP 에러 해결하는 방법 1
  13. 2008.05.14 chat Server
  14. 2008.05.14 간단한 ChatClient
  15. 2008.05.14 자바관련 잡다한 이야기 - 스레드의 동기화 문제 1
  16. 2008.05.13 자바관련 잡다한 이야기 - 네트워크와 스레드
  17. 2008.05.13 자바관련 잡다한 이야기 - 인터페이스와 다형성
  18. 2008.05.11 자바관련 잡다한 이야기 - 상속성과 다양성 핵심정리
  19. 2008.05.05 자바관련 잡다한 이야기 - static과 final
  20. 2008.05.03 자바관련 잡다한 이야기 - 인스턴스 변수와 지역 변수의 차이점
  21. 2008.05.01 자바관련 잡다한 이야기 - 객체의 행동
  22. 2008.05.01 자바관련 잡다한 이야기 - 클래스와 객체 핵심정리
  23. 2008.05.01 자바관련 잡다한 이야기 - 원시 변수와 레퍼런스
  24. 2008.04.29 Collection
  25. 2008.04.28 수행시간 측정 StopWatch Class

iodocs의 구조는 기본적으로 다음과 같다.


- iodocs-master 

- node_modules

 iodocs에서 사용하는 node의 모듈 디렉토리 

 

- public

 API의 정보들을 등록하는 디렉톨리

 

- views 

 iodocs의 문서 포멧 및 디자인을 지정 하는 디렉토리

 

- app.js 

 iodocs 내부적으로 API를 Call할 때 사용하는 javascript 

 

- config.json 

 iodocs를 띄우기 위한 기본 설정들

 - package.json 

 iodocs 버전 정보 



                      

node_modules

이곳에는 node.js에서 사용 가능한 모듈이 있다 만약 iodocs를 커스터마이징하여 추가적인 기능을 붙이고 싶은 경우 npm으로 추가 모듈을 설치하면 된다. 


public

해당 디렉토리 밑에는 총 3개의 하위 디렉토리가 있다.

  • data
  • images
  • javascripts
  • stylesheets




Posted by 서오석
,

API를 개발하면 문서를 만들어야 하는데 이것을 쉽게 만들어주는 툴이 iodocs이다. 

Git에 보면 간단한 설명이 있다.


I/O Docs Github 바로가기


I/O Docs is a live interactive documentation system for RESTful web APIs. By defining APIs at the resource, method and parameter levels in a JSON schema, I/O Docs will generate a JavaScript client interface. API calls can be executed from this interface, which are then proxied through the I/O Docs server with payload data cleanly formatted (pretty-printed if JSON or XML). Basic HTML text tags are enabled in the JSON schema.


Iodocs가 좋은 점은 doc인데도 불구하고 해당 페이지에서 직접 API를 날려 볼 수 있도록 되어 있다. 


설치 방법은 yum을 이용해서 간단하게 설치하는 방법도 있으나 여기서는 하나씩 설치하는 방식을 사용한다.


INSTALLATION INSTRUCTIONS FOR NODE, NPM & REDIS

  1. Node.js - https://github.com/joyent/node/wiki/Installation
  2. npm (Node package manager) - https://github.com/isaacs/npm
  3. Redis - http://redis.io/download

INSTALLATION INSTRUCTIONS FOR I/O DOCS

From the command line type in:

  git clone http://github.com/mashery/iodocs.git
  cd iodocs
  npm install


해당 사이트에 가면 위 1~3번을 설치하고 나면 간단하게 iodocs를 설치 할 수 있다고 나온다. 


일단은 저걸 설치하려면 Git이 서버에 설치가 되어 있어야 한다. 일단 Git을 설치하자.

OS버전은 RedHat이며 각 설치 방법은  OS별로 다르다. 


[root@iaas-5dol-sandbox ~]# yum install git-core



이제 node.js를 설치해보자. 

https://github.com/joyent/node/wiki/Installation 를 이용해서 직접 설치를 할 수 있으나.. 파이선을 버전에 맞게 다시 인스톨해줘야 하는 번거로움이 있어서 yum으로 인스톨을 하겠다.


필자의 경우 yum으로 install하려고 하니 nodejs가 없다고 나왔다.


그냥 바이너리를 이용해서 설치하고 환경변수에 추가해주자


[root@iaas-5dol-sandbox program] wget http://nodejs.org/dist/v0.10.30/node-v0.10.30-linux-x64.tar.gz

[root@iaas-5dol-sandbox program]# tar xvfz node-v0.10.30-linux-x64.tar.gz

[root@iaas-5dol-sandbox program]# ln -s node-v0.10.30-linux-x64 node

[root@iaas-5dol-sandbox ~]vi .bash_profile


PATH=$PATH:$HOME/bin:/daum/program/node/bin

export PATH



자 이제 redis를 설치하자. redis를 iodocs에서 사용하는 이유는 iodocs 내부적으로 문서로 만든 API 자체를 날려볼 수 있기 때문인데 api가 oauth를 사용하는 경우에 인증을 저장할 공간이 필요하고 그것을 redis에 저장한다.


만약 oauth를 사용하지 않는 경우는 redis를 제거해도 되긴 한다. 여기선 일단 redis를 적용한 버전으로 진행한다.


[root@iaas-5dol-sandbox program] wget http://download.redis.io/releases/redis-2.8.13.tar.gz

[root@iaas-5dol-sandbox program] tar xvfz redis-2.8.13.tar.gz

[root@iaas-5dol-sandbox program] make

[root@iaas-5dol-sandbox program] make install

[root@iaas-5dol-sandbox program] ln -s redis-2.8.13 redis



이제 기본적인 것은 다 설치했으니 iodocs를 설치해보자.

[root@iaas-5dol-sandbox program] git clone http://github.com/mashery/iodocs.git

[root@iaas-5dol-sandbox program] cd iodocs

[root@iaas-5dol-sandbox program] npm install





'개발 이야기 > 유용한 Coding' 카테고리의 다른 글

I/O Docs 구조  (0) 2014.08.18
java로 excel 다중 sheet 만들기  (0) 2010.06.03
jdbc 사용 예제  (1) 2009.08.25
Struts2 + Spring2.0 엮을 때 나는 NullPointerException 해결방법  (1) 2009.02.02
FTP 파일 업로드  (0) 2008.12.01
Posted by 서오석
,

우선 excel을 xml 포멧으로 다중 시트를 만들어야 하기 때문에 아래 포멧을 사용해야 한다.

xml 코드

위의 것은 간단한 예제 시트인데 설명을 하자면 다음과 같다.
<Author> : 작성자
<LastAuthor> : 마지막 작성자
<Created> : 생성일
<Company> : 회사
<Version> : 파일 버전
<Styles> : 셀의 style을 지정한다.
<Worksheet ss:Name="Sheet1"> 요게 시트의 구분이다 요 포멧으로 묶여 있는 애들이 1개의 시트가 된다.

xml에서 아래처럼 만들면

 <Worksheet ss:Name="Sheet1">
내용
</Worksheet>
<Worksheet ss:Name="Sheet2">
내용
</Worksheet>

엑셀파일을 엑셀로 열었을 때 다중시트가 생긴다.

설정할 때 주의해야할 것이 있는데

 <Table ss:ExpandedColumnCount="6" ss:ExpandedRowCount="65535"
   x:FullColumns="1" x:FullRows="1">

요기서 한 시트에 사용할 컬럼과 최대 row를 지정하는데
만약 엑셀의 데이터를 싣는
<Row> 의 갯수나 <cell>의 갯수가 저기 설정한 갯수보다 많으면

파일이 열리지 않는다.

일종의 최대 크기를 지정한다고 보면 된다.

'개발 이야기 > 유용한 Coding' 카테고리의 다른 글

I/O Docs 구조  (0) 2014.08.18
I/O Docs 설치하기  (0) 2014.08.18
jdbc 사용 예제  (1) 2009.08.25
Struts2 + Spring2.0 엮을 때 나는 NullPointerException 해결방법  (1) 2009.02.02
FTP 파일 업로드  (0) 2008.12.01
Posted by 서오석
,

회사에서는 iBatis를 주로 사용하는데 급 jdbc로 직접 데이터를 퍼와야 할 일이 생겼다.
(대용량의 데이터를 iBatis로 가져오다보니 메모리 때문에 서버가 뻗어버린다. --;; iBatis의 데이터 처리 문제라서 결국..ㅠㅠ)

근데 하도 오랜만에 jdbc를 써가지구 자꾸 까먹어 여따가 정리해놓는다.

예제는 그냥 멤버테이블에 갯수 가져오는 거다. (자세한 설명은 안한다..;;)



import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;


public class JdbcConnect {

 public static void main(String[] arg){
  String dbUrl = "jdbc:oracle:thin:@255.255.255.255:1521:orcl";           
  String dbUser = "5dol";                                                              
  String dbPassword = "story";
  String sql = null;
  ResultSet rs = null;
  
  Connection conn = null;
  //Statement stmt = null;
  PreparedStatement pstmt = null;
  
  try {
   Class.forName("oracle.jdbc.driver.OracleDriver");  //드라이버 로딩   
   conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);   
   //stmt = conn.createStatement();
   
   sql = "select count(*) num1 from member";
   
   pstmt = conn.prepareStatement(sql);
   
   rs = pstmt.executeQuery();
   
  while(rs.next()){
   System.out.println(rs.getString("num1"));
  }
   
  rs.close();
  conn.close();
   
  } catch (ClassNotFoundException e) {
   e.printStackTrace();
  } catch (SQLException e) {
   e.printStackTrace();
  }
  
 }
 
}

Posted by 서오석
,

Struts2하고 Spring2.0하고 엮어서 presentation Layer를 Struts로 사용하고 Business Layer를 Spring으로 사용하려고 세팅을 하고 Action을 만든 후에 Action에서 Spring bean을 사용하려고 하면 아래와 같은 에러가 발생하는 경우가 있다. 

에러 메시지
이 에러를 고치는데 별 짓을 다 해보다가 결국 struts 설정에 에러가 있다는 걸 알았다.. ㅠㅠ(이틀을 날려버린..--;)

우선 struts랑 spring이랑 붙이려면 라이브러리 파일이 하나 필요하다. (라이브러리가 struts2 버젼과 같아야 한다.)

이 라이브러리를 추가해주고 나서

struts.properties 안에 "struts.objectFactory" 활성화 해 준 후에

struts.objectFactory = org.apache.struts2.spring.StrutsSpringObjectFactory

이렇게 명시해준다. 이건 object를 스프링이 관리해주도록 하는 거이란다.

그리고 중요한 것! 저 에러가나는 원인은

struts.objectFactory.spring.autoWire = name   <- 이 부분 때문에 그렇다.

저 autoWire를 name으로 설정해서 에러가 나는 것이다. 이걸 type으로 바꾸면 된다. 

struts.objectFactory.spring.autoWire = type 

으로 변경해주면 제대로 에러없이 돌아간다.

이거 몰라서 개고생 했다..--;;
Posted by 서오석
,

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.net.SocketException;

import org.apache.commons.net.ftp.FTP;

import org.apache.commons.net.ftp.FTPClient;

import org.apache.commons.net.ftp.FTPReply;

 

public class adminFTP {

    

    private String server = "000.000.000.000";  //파일 업로드 할 서버 IP

    private String username = "DAUM";       //사용자 Id

    private String password = "DAUM";       //패스워드

    private String defaultPath = "/data/";     // 저장할 경로

    

/**

     * 파일을 업로드 해준다.

     * @param filePath  자신의 하드에 있는 파일의 경로를 말한다. 파일 경로랑 파일명까지다. ex: c:\\test.jpg

     * @param destfilePath FTP서버에 업로드할 경로를 말한다.

     *                     상단의 defaultPath로 기본 위치를 잡고 그 뒤에 경로와 파일명까지 붙여서 쓴다.

     * @return

     */

    public boolean upLoad(String filePath, String destfilePath){

        FTPClient ftpClient = new FTPClient();

        ftpClient.setControlEncoding("euc-kr");  

        try {

            ftpClient.connect(server);

            int reply = ftpClient.getReplyCode();

            if (!FTPReply.isPositiveCompletion(reply)) {

                ftpClient.disconnect();

                System.out.println("FTP server refused connection.");

            }  else {

                 System.out.println("Connect successful");

                 ftpClient.setSoTimeout(10000);               

                 ftpClient.login(username, password);

                 ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

                 ftpClient.changeWorkingDirectory(defaultPath);

                 File put_file = new File(filePath);

                 FileInputStream inputStream = new FileInputStream(put_file);

                 boolean result = ftpClient.storeFile(destfilePath, inputStream);

                 System.out.println("FILE TRANSPORT STATUS  :"+result);

                 inputStream.close();

                 ftpClient.logout();

            }

            

        } catch (SocketException e) {

            System.out.println(e);          

            e.printStackTrace();

            return false;

        } catch (IOException e) {

            System.out.println(e);

            e.printStackTrace();

            return false;

        }

        return true;        

    }


Posted by 서오석
,
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.webapp.WebAppContext;

public class Was {
 public static void main(String args[]){

  Server server = new Server();

  SocketConnector connector = new SocketConnector();

  connector.setPort(8000);
  server.setConnectors(new Connector[] { connector });

  ContextHandlerCollection contexts = new ContextHandlerCollection();

  server.setHandler(contexts);

  WebAppContext context = new WebAppContext();
  context.setResourceBase("D:\\workspace\\velocity\\WebRoot");
  context.setContextPath("/");
  contexts.addHandler(context);  

  try {

   server.start();

  } catch(Exception e) {
   e.printStackTrace();
  }
 } 

}
Posted by 서오석
,

Ant로 배포를 한 후 테스트를 할라고 하면 404가 뜨는 경우가 있다. 그래서 htdocs의 배포된 곳에 가면 배포된 파일은 죄다 없고 loader만 딸랑 남는 경우가 있는데 이건 아주 간단한 이유다.

만약 사용자가 배포하는 해당 폴더에 FTP로든 콘솔로든 접근을 해 있는 상태라면 배포가 안된다.
무슨 말인가 하면
test라는 프로젝트를 배포한다고 했을 때
/htdocs/test 가 배포의 위치라고 하자.

근데 만약 consol(z-term or putty)같은 거로 해당 디랙토리에 이미 접근해있다면( 사용자의 위치가 /htdocs/test/sample 라는 폴더에 있다면) 배포를 해도 배포가 안되고 배포된 플젝 폴더 안에는 Loader라는 녀석만 덩그러니 남는다.

간단히 그냥 consol을 종료하던 아니면 배포되는 위치를 벗어난 후 다시 배포를 하면 이상없이 배포가 된다.
Posted by 서오석
,

자바로 메일보내는 폼을 개발할 이유가 있었는데 원래 그냥 Sun에서 지원하는 Mail.jar를 이용한 메일보내기를 쓰다가 더 쉬운 걸 찾아냈다.



위의 3개 파일의 압축을 풀면 Mail.jar, activations.jar, commons-email-1.1.jar 이 나온다.

뭐 각 JAR에 대한 설명은 워낙 인터넷에 마구 떠돌아 다니니까 알아서 찾고 바로 쓰는 법부터 보자

우선 위의 세 jar을 라이브러리에 추가를 한다.

그리고 메일을 보내는 action이 있는 곳에 이렇게 적어보자.

public void sendMail(){

SimpleEmail email = new SimpleEmail(); // 선언을 한다
email.setCharset("UTF-8");     // 인코딩을 설정한다.
email.setHostName("smtp.daum.com");
email.setFrom("5dols.daum.com", "5dols"); //보내는 사람
email.setAuthentication("USER", "PASSWORD"); //SMTP 인증이 필요할 경우(없으면 지워버린다.)
email.addTo("5dols.naver.com", "5dolsstory"); //받는사람
email.setSubject("메일의 제목입니다."); //메일 제목
email.setMsg("메일의 내용입니다."); //메일 내용
email.send(); //메일 발송
}

여기서부터는 펌글




더 자세한 내용은 아래 링크를 따라가자~

http://grooveit.org/blog/?tag=commons-mail
Posted by 서오석
,

소트하고 싶은게 list라면

class EngNameComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    String en1 = ((Customer)o1).engName;
    String en2 = ((Customer)o2).engName;
    return en1.compareTo(en2); // ascending 정렬
  }


위의 형식과 같은 클래스 만들고~ (String 형으로 정렬 때)

class YoungOrderComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    int by1 = ((Customer)o1).birthYear;
    int by2 = ((Customer)o2).birthYear;
    return by1 > by2 ? -1 : (by1 == by2 ? 0 : 1); // descending 정렬.....
  }


이건 int 형으로 정렬 때


그리고 정렬하고 싶은 리스트 가 있는 곳에 이거 붙이면 끝~ (근데 왜케 수행속도가 안나오지..--;)

Collections.sort(list, new EngNameComparator());

Posted by 서오석
,

타일즈에서 extends가 이상하게 안될때는..

toolbox.xml 에 있는 노드의 경로들이 제대로 잘 박혀 있는지 확인해보자.
기본 default tool들은 괜찮은데, 보통 커스텀으로 박아넣은것들에서 오타가
날 확률이 많으니 조심하자.



삽펐던 사연은 아래 자세히..















=============================================================================


어제 타일즈 삽 펀 사연..



스프링이 대새인데.. 스트러츠로 그냥 하던대로 프로젝트 빌딩을 하고 있었다.

다 하고 나니 이상하게

tiles-defs.xml 에 설정한것과 다르게 동작을 했다.


빌딩과정..
struts-config.xml 에 타일즈 설정을 했고..
<plug-in className="org.apache.struts.tiles.TilesPlugin">
  <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml, /WEB-INF/tiles-defs-info.xml" />
  <set-property property="definitions-parser-validate" value="true" />
  <set-property property="moduleAware" value="true" />
 </plug-in>

라고 정상적으로 설정을 했고..




벨로시티로 타일즈를 구성한곳에 박아야 하니..
velocity.properties 에도 정상적으로 잘 등록을 했고..

velocimacro.library = VM_global_library.vm
velocimacro.permissions.allow.inline = true
velocimacro.permissions.allow.inline.to.replace.global = false
velocimacro.permissions.allow.inline.local.scope = false 
velocimacro.context.localscope = false

resource.loader = webapp

webapp.resource.loader.class = org.apache.velocity.tools.view.servlet.WebappLoader
webapp.resource.loader.path = /WEB-INF/template/
webapp.resource.loader.cache = false
webapp.resource.loader.modificationCheckInterval = 60

input.encoding=UTF-8
output.encoding=UTF-8
default.contentType=text/html;charset=utf-8

directive.foreach.counter.name = velocityCount
directive.foreach.counter.initial.value = 1




web.xml 에도 vm파일을 사용할 수 있도록 정상적으로 등록을 하고..
<servlet>
  <servlet-name>velocity</servlet-name>
  <servlet-class>
   org.apache.velocity.tools.view.servlet.VelocityViewServlet
  </servlet-class>
  <init-param>
   <param-name>org.apache.velocity.toolbox</param-name>
   <param-value>/WEB-INF/toolbox.xml</param-value>
  </init-param>
  <init-param>
   <param-name>org.apache.velocity.properties</param-name>
   <param-value>/WEB-INF/velocity.properties</param-value>
  </init-param>
  <load-on-startup>10</load-on-startup>
 </servlet>

<servlet-mapping>
  <servlet-name>velocity</servlet-name>
  <url-pattern>*.vm</url-pattern>
 </servlet-mapping>



toolbox.xml 에도 잘 설정을 한듯했다..


그런데 어쩐지 tiles-defs.xml 과 tiles-defs-info.xml 요렇게 2개로 갈라서
처리하려고 했는데..

 tiles-defs.xml
<tiles-definitions>

 <!-- 1단 레이어  -->
 <definition name="layout_onecolumn.tiles" path="/layout/layout_onecolumn.vm">
  <put name="tab" value="/layout/top_tab.vm" />
  <put name="bottom" value="/layout/bottom.vm" />
 </definition>

 <!-- Blank 레이아웃 -->
 <definition name="layout_blank.tiles" path="/layout/layout_blank.vm" />
  <definition name="blank.tiles" path="/layout/blank2.vm" />

</tiles-definitions>




tiles-defs-info.xml
<tiles-definitions>

  <definition name="survey.top.tiles" extends="blank.tiles">
  <put name="content" value="/top/topContent.vm" />
 </definition>
 
 <definition name="survey.survey.tiles" extends="layout_onecolumn.tiles">
  <put name="content" value="/test/topContent.vm" />
 </definition>

</tiles-definitions>



이렇게 설정해서  blank.tiles를 extends 하려고 했는데.. extends를 하는 놈은
안중에도 없고, tils-defs.xml 에 있는 blank.tiles name을 가진 녀석의
/layout/blank2.vm 파일 내용만 보여지는것이다. extends 한녀석도 같이 박혀야
하는데..

그래서 vm파일을 옮겨도 보고, 바꿔도 보고.. 타일즈 문법이 틀렸는지 확인도 해보며
수십번 삽질하다가..

toolbox.xml 의 어떤 한 노드의 경로에 오타가 있단느것을 발견해서
경로를 제대로 잡아주니, extends 가 안되던 것이 에러가 해결됐다..
<!-- Struts -->
 <tool>
  <key>tiles</key>
  <scope>request</scope>
  <class>org.apache.velocity.tools.struts.TilesTool</class>
 </tool>
 
 
 <!-- Custom -->
 <tool>
  <key>strUtil</key>
  <scope>application</scope>
  <class>net.coader.util.velocity.StringUtilsTool</class>
 </tool>

class의 경로에 오타가 있다거나 틀리면 extends문제가 발생한다.
보통 사용자가 java를 통해 custom tool을 만들때 경로를 집어 넣다가 발생할 수 있는
에러 이다.


이건 뭐 차라리 아예 안되서 에러를 내든지 하면 첨부터 설정을 봤을텐데 말이다..
삽질한 내 시간을 돌려줘 ㅠ_ㅠ 


- 내 사수인 종민님 블로그에서 퍼온 것임 -

'개발 이야기 > 유용한 Coding' 카테고리의 다른 글

Java로 쉽게 메일 보내기 메소드 만들기  (0) 2008.11.17
Collections.sort 로 쉽게 소트하기  (0) 2008.11.17
ANT FTP 에러 해결하는 방법  (1) 2008.07.28
chat Server  (0) 2008.05.14
간단한 ChatClient  (0) 2008.05.14
Posted by 서오석
,
이클립스에서 ant java.lang.NoClassDefFoundError: org/apache/commons/net/ftp/FTPClient

이클립스에서 ANT를 이용해 FTP 로 올릴려고 build.xml 작성하는 도중에 에러발생!!


미친에러 자식..--;

에러 : java.lang.NoClassDefFoundError: org/apache/commons/net/ftp/FTPClient

해결 방법 : commons-net-1.4.0.jar을 이클립스 window - preferences - ant - runtime - classpath - ant home entries에 추가해 준다.

Posted by 서오석
,

본 코드는 Head first Java의 예제 소스이다.

import java.io.*;
import java.net.*;
import java.util.*;

public class VerySimpleChatServer {
  ArrayList clientOutputStreams;
 
  public class ClientHandler implements Runnable{
   BufferedReader reader;
   Socket sock;
   
   public ClientHandler(Socket clientSocket){
    try{
     sock = clientSocket;
     InputStreamReader isReader = new InputStreamReader(sock.getInputStream());
     reader = new BufferedReader(isReader);
     
    }catch(Exception ex){
     ex.printStackTrace();
    }
   
   }
   
   public void run(){
    String message;
    try{
     while((message = reader.readLine()) != null){
      System.out.println("read "+ message);
      tellEveryone(message);
     }
    }catch(Exception ex){
     ex.printStackTrace();
    }
   
   }
   
  }
  public static void main(String[] args){
   new VerySimpleChatServer().go();
  }
 
  public void go(){
   clientOutputStreams = new ArrayList();
   try{
    ServerSocket serverSock = new ServerSocket(5000);
    while(true){
     Socket clientSocket = serverSock.accept();
     PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
     clientOutputStreams.add(writer);
     
     Thread t = new Thread(new ClientHandler(clientSocket));
     t.start();
     System.out.println("got a connection");    
    }
   
   }catch(Exception ex){
    ex.printStackTrace();
   }
   
  }
 
  public void tellEveryone(String message){
   Iterator it = clientOutputStreams.iterator();
   while(it.hasNext()){
    try{
     PrintWriter writer = (PrintWriter)it.next();
     writer.println(message);
     writer.flush();
    }catch(Exception ex){
     ex.printStackTrace();
    }
   }
  } 

}

Posted by 서오석
,

아래 코드는 Head first Java의 예제이다.

import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;

import com.sun.corba.se.spi.servicecontext.SendingContextServiceContext;

import java.awt.*;
import java.awt.event.*;

public class SimpleChatclient {
 JTextArea incoming;
 JTextField outgoing;
 BufferedReader reader;
 PrintWriter writer;
 Socket sock;
 
 public static void main(String[] args){
  SimpleChatclient client = new SimpleChatclient();
  client.go();
 }
 
 public void go(){
  JFrame frame = new JFrame("Simple Chat Client");
  JPanel mainPanel = new JPanel();
  incoming = new JTextArea(15,50);
  incoming.setLineWrap(true);
  incoming.setWrapStyleWord(true);
  incoming.setEditable(false);
  JScrollPane qScroller = new JScrollPane(incoming);
  qScroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
  qScroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
  outgoing = new JTextField(20);
  JButton sendButton = new JButton("Send");
  sendButton.addActionListener(new SendButtonListener());
  mainPanel.add(qScroller);
  mainPanel.add(outgoing);
  mainPanel.add(sendButton);
  setUpNetworking();
 
  Thread readerThread = new Thread(new IncomingReader());
  readerThread.start();
 
  frame.getContentPane().add(BorderLayout.CENTER, mainPanel);
  frame.setSize(400, 500);
  frame.setVisible(true);  
 }
 
 private void setUpNetworking(){
  try{
   sock = new Socket("127.0.0.1",5000);
   InputStreamReader streamReader = new InputStreamReader(sock.getInputStream());
   reader = new BufferedReader(streamReader);
   writer = new PrintWriter(sock.getOutputStream());
   System.out.println("networking established");
  }catch(IOException ex){
   ex.printStackTrace();
  }
 }
 
 public class SendButtonListener implements ActionListener{
  public void actionPerformed(ActionEvent ev){
   try{
   writer.println(outgoing.getText());
   writer.flush();
   }catch(Exception ex){
    ex.printStackTrace();
   }
   outgoing.setText("");
   outgoing.requestFocus();
  }
 }
 
 public class IncomingReader implements Runnable{
  public void run(){
   String message;
   try{
    while((message = reader.readLine())!= null){
     System.out.println("read "+ message);
     incoming.append(message+ "\n");
     }
    }catch(Exception ex){
     ex.printStackTrace();
    }
   }
  }
 }

Posted by 서오석
,

Thread.sleep()이라는 정적 메소드는 적어도 sleep 메소드에 전달된 인자로 지정한 시간 동안 스레드를 실행중인 상태를 떠나있게 만든다. 값에 100을 주면 100밀리세컨드 동안 스레드가 멈춘다.

sleep()메소드에서는 확인 예외(interruptedException)를 던지기 때문에 sleep()을 호출할 때에는 반드시 try/catch로 감싸거나 예외를 선언해야 한다.

스레드 두 개 이상이, 힙에 있는 동일한 객체를 접근하는 경우에 심각한 문제가 생길 수 있습니다.
 
스레드를 두 개 이상에서 똑같은 객체에 접근하면 데이터가 엉망이 될 수 있다. 예를들어, 한 스레드가 객체의 중요한 상태를 조작하는 도중에 실행중인 상태에서 벗어나면 심각한 문제가 생길 수 있다.

스레드를 사용할 때도 객체를 안전하게 만들고 싶다면 어떤 선언문들이 원자적인 절차로 처리되어야 하는지 결정해야 한다. 즉 다른 스레드가 같은 객체의 같은 메소드에 들어가기 전까지 끝까지 실행되어야만 하는 메소드를 결정해야 한다.

스레드 두개가 메소드 하나에 동시에 들어가는 일을 방지하고 싶다면 메소드 선언부에 synchronized 키워드를 추가해야 한다.

모든 객체에는 자물쇠가 하나씩 있으며 그 자물쇠에는 열쇠가 하나뿐이 없다. 대부분의 경우에 그 자물쇠에 대해서 신경을 쓸 필요가 없지만 객체에 동기화된 메소드가 있으면 자물쇠가 중요한 역할을 한다.

스레드에서 어떤 동기화된 메소드로 들어가려면 그 객체에 대한 열쇠가 있어야 한다. 열쇠가 없으면 그 스레드는 대기실 같은 공간으르 들어가서 열쇠를 슬 수 있을 때 까지 기다려야 한다. 이는 운영체제의 세마포와 비슷하다.

객체에 동기화된 메소드가 두개 이상 있어도 열쇠는 여전히 하나뿐이 없다. 어떤 스레드가 그 객체에 동기화된 메소드에 들어가면 다른 어떤 스레드도 같은 객체에 있는 동기화된 메소드에 들어갈 수 없다. 이런 제한이 있어야 데이터를 조작하는 모든 메소드를 동기함으로 데이터를 보호할 수 있다.

Posted by 서오석
,
네트워크 Socket 통신

  • 클라이언트와 서버 APP는 Socket 연결을 통해서 통신한다.
  • Socket은 서로 다른 물리적인 시스템 두 개에서 실행될 가능성이 있는 애플리케이션 두 개사이의 연결을 나타낸다.
  • 클라이언트는 서버 애플리케이션의 IP주소와 TCP포트 번호를 알아야 한다.
  • TCP포트는 특정 서버 애플리케이션에 할당된 16비트 부호가 없는 정수이다. TCP 포트 번호는 서로 다른 클라이언트가 똑같은 시스템에 접속하여 그 시스템에서 돌아가고 있는 서로 다른 애플리케이션과 통신을 할 수 있게 해주는 역할을 한다.
  • 클라이언트에서 서버  Socket을 만드는 방법으로 서버에 연결을 한다.
    Socket s = new Socket("127.0.0.1",4200);
  • 일단 연결되고 나면 클라이언트는 소켓으로부터 입력 및 출력 스트림을 얻을 수 있다. 이런 스트림은 저수준 '연결' 스트림이다.
    sock.getInputStream();
  • 서버로부터 텍스트 데이터를 읽고 싶다면 Socket으로부터 가져온 입력 스트림에 연쇄된 InputStreamReader와 이와 연쇄된 BufferReader를 만들면 된다.
  • InputStreamReader는 바이트를 받아서 텍스트 데이터로 변환해주는 '다리' 역할을 하는 스트림이다. 주로 고수준의 BufferedReader와 저수준의 Socket 입력 스트림 사이에 들어가는 가운데 고리 역할을 한다.
  • 서버로 텍스트 데이터를 보낼 때는 소켓의 출력 스트림에 직접 연쇄된 PrintWriter를 만들면 된다. 이 객체의 Print() 또는 println()메소드를 호출하면 서버로 String을 보낼 수 있다.
  • 서버에서는 특정 포트 번호로 들어오는 클라이언트 요청을 기다리기 위해  ServerSocket을 사용한다.
  • ServerSocket으로 요청이 들어오면 그 클라이언트와 Socket 연결을 함으로써 그 요청을 수락한다.

스레드

  • thread는 자바에서의 실행 스레드를 의미한다.
  • 자바에서는 스레드마다 각각의 호출 스택이 있다.
  • 대문자 T로 시작하는 Therad는 java.lang.Thread 클래스를 의미한다. Thread 객체는 실행 스레드를 나타낸다.
  • Thread에는 처리할 작업, 즉 해야할 일이 있어야 한다. Thread에서 처리할 작업은 Runnable  인터페이스를 구현하는 클래스의 인스턴스로 지정할 수 있다.
  • Runnable 인터페이스에는 메소드가 run() 하나밖에 없다. 새로운 Call stack의 맨 밑으로 들어가는 것이 바로 이 메소드이다. 즉 새롱누 스레드에서 가장 먼저 실행되는 것이 바로 run()메소드이다.
  • 새로운 스래드를 시작하려면 Thread의 생성자에 전달 할 Runnable 객체가 필요하다.
  • Thread의 인스턴스를 만들긴 했는데 아직 start() 메소드를 호출하지 않았으면 그 스레드는 아직 새 스레드 상태에 있다고 부른다.
  • (Thred 객체의  start() 메소드를 호출하여) 스레드를 시작하면 새로운 stack이 생성되며 Runnable의 Run()메소드가  stack 맨 아래에 들어간다. 그러면 그 스레드는 이제 실행되기를 기다리고 있는 실행 가능한 상태가 된다.
  • JVM의 스레드 스케줄러에 의해 현재 실행중인 스레드로 선택 받으면 그 스레드는 실행중인 상태가 된다. 프로세서가 하나뿐인 시스템에서는 현재 실행중인 스레드가 하나밖에 있을 수 없다.
  • 스레드가 실행 중인 상태에서 봉쇄된 상태로 옮겨지는 경우도 있다. 스트림으로부터 들어오는 데이터를 기다리고 있을 때, 대기 상테로 들어갔을 때, 객체에 대한 잠금이 해제되기를 기다리고 있을 때와 같은 상황에서 스레드가 봉쇄돌 수 있다.
  • 스레드 스케줄링은 어떤 특정한 방식으로 작동한다는 보장이 없기 때문에 모든 스레드가 공평하게 기회를 부여받을 수 있으리라는 가정은 하지 말아야 한다. 스레드를 주기적으로 대기 상태로 전화시키는 방식으로 순번이 돌아가는 것에 영향을 미칠 수는 있다.


스레드 예제 소스

public class MyRunnable implements Runnable {
 public void run(){
  go();
 }
 
 public void go(){
  System.out.println("-0-;");
 }
}
                                                                                                                                     
public class ThreadTester {
 public static void main(String[] args){
  Runnable threadJob = new MyRunnable();
  Thread myThread = new Thread(threadJob);
 
  myThread.start();
 }

}

Posted by 서오석
,
  • 클래스를 만들 때 인스턴스를 만들 수 없게 하고 싶다면 (즉, 그 클래스 유형의 객체를 만들 수 없게 하고 싶다면) abstract 키워드를 사용하면 된다.
  • 추상 클래스에는 추상 메소드와 추상 메소드가 아닌 메소드를 모두 집어넣을 수 있다.
  • 클래스에 추상 메소드가 하나라도 있으면 그 클래스는 추상 클래스로 지정해야 한다.
  • 추상 메소드는 본체가 없으며 선언 부분은 세미콜론으로 끝난다.
  • 상속 트리에서 처음으로 나오는 구상 클래스에서는 반드시 모든 추상 메소드를 구현해야 한다.
  • 자바에 들어있는 모든 클래스는 직접 또는 간접적으로 Object의 하위 클래스입니다.
  • ArrayList에서 꺼내는 모든 객체는 Object 유형입니다.(즉, Object 레퍼런스 변수로만 참조 할 수 있다,)
  • 레퍼런스 변수를 캐스트해서 객체의 원래 유형으로 돌려놓을 수 있다.
  • 어떤 객체에 있는 메소드를 호출하려면 실제 객체의 유형과는 관계없이 그 메소드가 레퍼런스 변수로 쓰인 클래스(또는 인터페이스)에 들어있는 메소드여야만 한다.
  • DDD와 관련된 문제 때문에 자바에서는 다중 상속을 허용하지 않는다. 클래스는 단 하나만 확장할 수 있다.(즉 직속 상위 클래스는 하나밖에 없다.)
  • 인터페이스는 100% 순수한 추상 클래스입니다. 인터페이스에서는 추상 메소드만 정의한다.
  • 인터페이스를 만들 때는 Class 대신 interface라는 키워드를 사용한다.
  • 인터페이스를 구현할 때는 implements라는 키워드를 쓰면 된다.
  • 클래스를 만들 때 인터페이스를 여러 개 구현할 수 있다.
  • 인터페이스의 모든 메소드는 자동으로 public 메소드, 그리고 abstract 메소드가 되기 때문에 인터페이스를 구현하는 클래스에서는 인터페이스에 들어있는 모든 메소드를 구현해야한다.
Posted by 서오석
,
  • 하위 클래스는 상위클래스를 확장합니다.
  • 하위클래스는 상위 클래스에 있는 모든 public으로 지정한 인스턴스 변수와 메소드를 상속합니다. 하지만 private로 지정한 인스턴스 변수와 메소드는 상속하지 않습니다.
  • 메소드는 오버라이드할 수 있지만 인스턴스 변수는 오버라이드 할  수 없습니다. (하위클래스에서 재정의 할 수는 있지만 오버라이드하는 것과는 다르다. 그리고 사실 오버라이드 할 필요를 거의 못느낀다.)
  • 하위클래스에서 메소드를 오버라이드하면, 그리고 하위클래스의 인스턴스에 대해 그 메소드를 호출하면 오버라이드된 버전의 메소드가 호출된다.
Posted by 서오석
,

Static

자바에서 static으로 선언하면 해당 메서드나 변수는 정적이된다. 만약 클래스의 변수를 static으로 선언하게 되면 그 변수는 객체의 변수가 되는 것이 아니라 클래스의 변수가 된다. 클래스의 변수이기 때문에 어떤 객체라도 동일한 주소의 변수를 참조하게 된다.

static의 특징

  • 객체를 생성해도 static 변수는 메모리에 하나만 생성된다.
  • 다른 JVM에서는 선언을 하면 다른 주소나 다른 값을 참조하지만 같은 JVM이나 WAS 인스턴스에서는 같은 주소와 같은 값을 참조한다.
  • CG의 대상도 되지 않는다.
  • 지역변수에 static을 사용할 수 없다. (매서드 안에서 사용 불가능)

만약에 properties파일을 많이 사용한다면 차라리 static으로 읽어서 관리하는 것도 좋다. 왜냐하면 매번 클래스의 객체가 만들어질 때 properties를 불러오려면 부하가 많이 걸리기 때문이다.

조심해야할 것
위에 말했듯이 static은 CG 대상이 아니다. 그렇기 때문에 잘못하면 Memory Leak 현상이 발생하게 된다. 이는 시스템의 메모리를 마구 먹어서 결국은 OutofMemoryError을 발생시킬 수 있다. (Collection을 Static으로 잡고 안에 아무 데이터나 마구 넣은다음 돌려보길 바란다. 그럼 바로 GG..)

final

final은 한번 final로 지정한 값은 변하지 않는다. 변수 앞에 final이 붙으면 상수를 의미한다.


Posted by 서오석
,
1. 인스턴스 변수는 클래스 내에서 선언된다. 메소드 내에서 선언되는 것이 아니다.
ex)
Class Student {
    private int studentNumber = 200210937;
    private String name = "5dols";
 //
}

2. 지역 변수는 메소드 내에서 선언된다.
ex)
Class Student {
    int studentNumber = 200210937;
    String name = "5dols";
  
    public int add() {
        int addstuNumber = studentNumber + 1;
        return addstuNumber;
    }
}

3. 지역 변수는 사용하기 전에 반드시 초기화해야 한다.
Class Student {
    int studentNumber = 200210937;
    ....
}
Posted by 서오석
,

메소드와 인스턴스 핵심정리

  • 클래스에서는 객체가 아는 것과 객체가 하는 것을 정의한다.
  • 인스턴스 변수는 각체가 아는 것이다.
  • 메소드는 객체가 하는 것이다.
  • 메소드에서 인스턴스 변수를 이용하여 같은 유형의 객체가 다른 식으로 행동하게 할 수있다.
  • 전달하는 값의 개수와 유형은 반드시 메소드를 선언할 때 지정한 것과 같아야 하며 그 순서도 같아야 한다.
  • 메소드 안팎으로 전달되는 값은 상황에 따라 자동으로 더 큰 유형으로 올라갈 수 있다. 더 작은 유형으로 바꿔야 한다면 강제로 캐스팅을 해야 한다.
  • 메소드에 인자를 전달할 때는 리터럴값을 사용할 수 있고 선언된 매개변수 유형의 변수를 사용할 수 있다.
Posted by 서오석
,
클래스와 객체 핵심정리

  • 모든 자바 코드는 클래스 내에서 정의된다.
  • 클래스는 해당 클래스 유형의 객체를 만드는 방법을 설명하는 역항르 한다. 클래스는 청사진과 같다.
    객체는 각자 알아서 자기 할 일을 처리할 수 있다. 사용자는 객체에서 작업을 처리하는 방법에 대해서는 신경쓰지 않아도 된다.
  • 객체에는 알고 있는 것할 수 있는 것이 있다.
  • 객체가 자기 자신에 대해 알고 있는 것은 인스턴스 변수라고 부른다. 객체의 상태를 나타낸다.
  • 객체가 할 수 있는 것은 메소드라고 부른다. 객체의 행동을 나타낸다.
  • 클래스를 새로 만들 때는 그 클래스 유형의 객체를 만들어서 테스트하는 테스트용 클래스를 따로 만드는 것도 좋다.
  • 클래스에서는 덜 구체적인 상위클래스로부터 인스턴스 변수와 메소드를 상속 할 수 있다.
Posted by 서오석
,
원시 변수와 레퍼런스

가비지 컬렉션 기능이 있는 힙에서의 삶

Book b = new Book();
Book c = new Book();
사용자 삽입 이미지
Book 객체 두 개는 Heap에서 살게 되었다.

레퍼런스: 2개
객체: 2개
-----------------------------------------------------------------------------------------
Book d = c; 이러면
사용자 삽입 이미지




















c와 d는 똑같은 객체를 참조한다.

레퍼런스: 3개
객체: 2개
-----------------------------------------------------------------------------------------
c = b; 이러면
사용자 삽입 이미지

b와 c는 모두 같은 객체를 참조한다.

레퍼런스: 3개
객체: 2개
-----------------------------------------------------------------------------------------

힙에서의 삶과 죽음

Book b = new Book();
Book c = new Book();
사용자 삽입 이미지

Book 객체 두개가 힙에서 살게 되었다.

활성 레퍼런스: 2개
접근할 수 있는 객체: 2개

-----------------------------------------------------------------------------------------

b= c; 이러면
사용자 삽입 이미지

b와 c는 모두 같은 객체를 참조한다. 1번 객체는 버림받았기 때문에 GC의 대상이 된다.

활성 레퍼런스: 2개
접근할 수 있는 객체: 1개
버림받은 객체: 1개

1번 객체는 아무 레퍼런스도 남아있지 않아서 접근도 할 수 없다.
-----------------------------------------------------------------------------------------

c= null; 이러면
사용자 삽입 이미지

2번 객체에는 여전히 활성 레퍼런스(b)가 있으며 활성 레퍼런스가 있는 한 GC대상이 되진 않는다.

활성 레퍼런스: 1개
null 레퍼런스: 1개
접근 할 수 있는 객체: 1개
버림받은 객체: 1개


Posted by 서오석
,

Set 인터페이스

  • HashSet : 데이터를 해쉬 테이블에 담는 클래스로 순서 없이 저장된다.
  • TreeSet : red-black 트리에 저장되며 값에 따라 순서가 정해진다. HashSet보다 느리지만 데이터를 담는 동시에 정렬할 때 유용하다.
  • LinkedHashSet : 해쉬 테이블에 데이터를 담는데 저장된 순서에 따라 순서가 결정된다.

데이터 속도는 비슷하지만 데이터를 꺼낼 때 속도는 TreeSet이 가장 느리고 LinkedHashSet이 가장 빠르다.

List 인터페이스

  • Vector : 크기를 객체 생성시에 지정할 필요가 없는 배열 클래스다.
  • ArraryList : Vector와 비슷하지만, 동기화 처리가 되어 있지 않다.
  • LinkedList : ArrayList와 같지만 Queue 인터페이스를 구현했기 때문에 FIFO 큐 작업을 한다.

Vector가  가장 빠르며 나머지 두개는 비슷하다.

Map 인터페이스

  • HashMap : 데이터를 Hash Table에 담는 클래스다. Hashtable과 다른 점은  NULL값을 허용한다는 것과 동기화가 되어있지 않다는 것이다.
  • Hashtable : 데이터를 Hash Table에 담는 클래스다. 내부에서 관리하는 해쉬 테이블 객체가 동기화되어 있으므로 동기화가 필요한 부분에서는 이 클래스를 사용하기 바란다.
  • TreeMap : red-black 트리에 데이터를 담는다. TreeSet과 다른 점은 키에 순서가 정해진다는 것이다.
  • LinkedHashMap : HashMap과 거의 동일하며 Double Linked List 방식을 사용하여 데이터를 담는다는 점만 다르다.

TreeMap 클래스가 가장 느리며 나머지 3개는 비슷한 속도를 보인다.


속도를 관련해서 왠만해선 속도 차이가 별로 없다.

Posted by 서오석
,

만약 자신이 만든 메서드나 클래스의 수행속도를 알고 싶다면 System.nanoTime을 이용해서 속도를 체크할 수 있다. 물론 프로파일링 툴을 써도 되지만 그냥 코드만으로 단순하게 사용하고 싶은 경우 시간측정용 메서드를 만들 수 있다.


코드는 아래와 같다.

public class StopWatch {
 long startTime;
 long elapsedTime=0;
 StringBuffer currentName;
 boolean threadFlag = false;
 
 public StopWatch(boolean threadFlag){
 currentName = new StringBuffer();
 startTime = System.nanoTime();
 }
 
 public StopWatch(String message){
  changeMessage("",true,true);
 }
 
 public StopWatch(String message, boolean threadFlag){
  changeMessage(message, false, true);
 }
 
 public void changeMessage(String message, boolean threadFlag, boolean resetFlag){
  StringBuffer threadName= new StringBuffer();
  this.threadFlag = threadFlag;
  if(threadFlag){
   threadName.append("ThreadName=").append(Thread.currentThread().getName());  
  }
  currentName.append("[").append(message).append(threadName).append("]");
  if(resetFlag){
   start();
  }
 }
 
 public void start(){
  startTime = System.nanoTime();
  elapsedTime = 0;
 }
 
 public void stop(){
  elapsedTime = System.nanoTime() - startTime;  
 }
 
 public double getElapsedMS(){
  if(elapsedTime ==0)
   stop();
  return elapsedTime/100000.0;
 }
 
 public double getElapsedNano(){
  if(elapsedTime ==0)
   stop();
  return elapsedTime;
 }
 
 public String toString(){
  if(elapsedTime ==0)
   stop();
  currentName.append("elapsed Time: ").append(elapsedTime/1000000.0).append("ms");
  return currentName.toString();
 }
}

Posted by 서오석
,