'자바 프로그래머'라면 한번쯤 고민하게 되는 부분이 있다. 그 중 하나가 자바 API가 지원하지 못하는 부분에 대한 것이다. 예를 들어 자바 API로는 네트워크 인터페이스 카드(NIC, 흔히 랜 카드라고 부른다)의 맥 어드레스(MAC Address)를 알아 낼 방법이 없다. 결국 JNI(Java Native Interface)를 사용하거나 다른 꽁수(?)를 찾아내곤 한다. 본인이 생각하기에는 두 방법 모두 시원스런 방법은 아니라서 상당히 거시기한 느낌이다. JNI를 사용하자니 C/C++ 코드를 사용하여 플랫폼에 제한적이 되어버리고… 꽁수를 사용하자니 안 먹히는 경우가 너무 많고…
어쨌거나 다른 해결 방법이 없다면 둘 중 하나를 사용해야 한다. 두 가지 중에서 본인이 많이 사용하는 방법은 꽁수를 사용하는 방법이다. 여기서 말하는 꽁수란 원하는 결과를 얻을 수 있는 외부 프로세스를 실행시키고 그 결과를 받아내어 필요한 부분만 얻어내는 방법을 사용하는 것이다. 이번 기사에서는 윈도우 XP 운영체제의 시스템 비밀스런(?) 정보를 얻어내는 자바 프로그램을 만들어 보도록 하자.
※ 주의: 본 소스의 모든 내용은 윈도우 XP 프로페셔널에서만 동작한다. 윈도우 XP 홈에디션에는 시스템 정보를 얻어내는 프로그램(systeminfo.exe)이 지원되지 않는다.
일단 화면에 도스창을 하나 띄워보자. 그런 후, "systeminfo"라고 입력하면 자신이 사용하는 윈도우 시스템에 대한 여러 정보가 나타난다. 윈도우 XP 프로페셔널에 이런 프로그램이 있다는 사실은 본인도 이 프로그램을 작성하면서 처음 알게 된 것이다. 자세한 세부 사항은 "systeminfo /?"라고 입력하여 도움말을 참고하도록 하자.
화면에 보이는 결과를 살펴보면 의외로 많은 사실들을 알 수 있다. 운영체제의 자세한 버전이나 사용자 정보, 실제 메모리 정보, 운영체제에 적용된 패치 등등… 이제부터는 이 정보들을 이용하여 자신이 원하는 몇 가지만을 보여주는 간단한 프로그램을 만들어보자.
이 프로그램의 핵심은 자바 프로그램 내에서 외부 프로그램을 실행시키고 그 결과를 받아내는 것이다. 이미 알고 있는 사람들에게는 아주 쉬운 일이지만 한번도 사용해보지 않았다면 이번 기회에 익혀두도록 하자. 나중에 두고 두고 써먹을 수 있는 프로그램 기법이 될 것이다(실제로 필자도 위기가 닥칠 때마다 이용해서 아주 유용하게 사용하고 있다). 아래 자바 메소드는 실행할 외부 프로그램의 이름을 스트링으로 지정하고 그 프로그램을 실행 후에 표준 출력과 표준 에러를 받아 내는 것이 전부이다.
public void run() { Runtime runtime = Runtime.getRuntime(); Process process = null;
try { // this.command = "systeminfo.exe"; process = runtime.exec(this.command); // 외부 프로그램 실행 } catch (IOException ioe) { ioe.printStackTrace(); }
// 표준 출력 InputStream standardOutput = process.getInputStream(); // 표준 에러 InputStream standardError = process.getErrorStream();
InputStreamReader ir = new InputStreamReader(standardOutput); InputStreamReader ow = new InputStreamReader(standardError);
BufferedReader outReader = new BufferedReader(ir); BufferedReader errorReader = new BufferedReader(ow);
StringBuffer stdout = new StringBuffer(); StringBuffer stderr = new StringBuffer(); String line = null; try { // 루프를 돌면서 퓨준 출력과 표준 에러를 계속 받아낸다 while ( (line = outReader.readLine()) != null) { stdout.append(line).append("\n"); } while ( (line = errorReader.readLine()) != null) { stderr.append(line).append("\n"); } standardOutput.close(); standardError.close(); } catch (IOException ioe) { ioe.printStackTrace(); } result.setStdout(stdout.toString()); // 이 결과를 나중에 사용한다 result.setStderr(stderr.toString()); }
위의 코드 마지막 부분에 나중에 사용할 결과를 지정하는 부분이 있다. 그 결과를 이용해서 원하는 정보만 가려내는 코드는 다음과 같다.
String result = pRunner.result.getStdout(); StringTokenizer st = new StringTokenizer(result, "\n"); while (st.hasMoreElements()) { String line = (String)st.nextElement(); if (line.startsWith("호스트 이름")) { addSysInfo(line); } if (line.startsWith("OS 이름")) { addSysInfo(line); } if (line.startsWith("등록된 소유자")) { addSysInfo(line); } if (line.startsWith("Product ID")) { addSysInfo(line); } if (line.startsWith("System Up Time")) { addSysInfo(line); } }
원하는 정보를 모두 얻었다면 이제 GUI로 아름답게 보여주는 일만 남았다. 이 부분에서는 여러 분들이 직접 원하는 형태대로 만들어보기 바란다. <출처:ibm.com/developerworks/kr>
자바 프로그래머라면 당연히 AWT나 Swing 컴포넌트에 대해서 알고 있고 GUI를 위해서 여러 형태의 컴포넌트들을 조합해서 원하는 화면을 만들어 보았을 것이다. 그러나 때로는 JDK 에서 지원하는 표준 컴포넌트들만으로는 무엇인가 부족함을 느껴 본적은 없는가? 자신의 입맛에 딱 맞는 컴포넌트가 없어서 오랜 시간을 허비하거나 고생해본 경험이 있을 수도 있다. 이번 기사에서는 자신만의 간단한 컴포넌트를 작성해보기로 하자.
이번 소스에서 만들것은 CPU의 사용양을 윈도우의 작업 관리자에서 보이는 형태처럼 만드는 것이다. JProgressBar를 사용하면 비슷한 형태가 나오기는 하지만 아무래도 많이 다르다는 느낌이 들어서 직접 만들어서 사용하자.
public class PmTickChartPanel extends JPanel { BorderLayout borderLayout1 = new BorderLayout(); JPanel centerPanel = new JPanel(); JPanel southPanel = new JPanel(); JPanel northPanel = new JPanel(); JLabel dataLabel = new JLabel(); GridLayout gridLayout1 = new GridLayout(); BorderLayout borderLayout2 = new BorderLayout(); JLabel titleLabel = new JLabel(); BorderLayout borderLayout3 = new BorderLayout(); JPanel westPanel = new JPanel(); JPanel eastPanel = new JPanel();
// 편의상 눈금은 10개만 보이고 색상 변경도 10 단위로만 int tickCount = 10;
for (int i = 0; i < tickCount; i++) { Tick tick = new Tick(); // 눈금(tick)을 10개 생성하여 추가 this.centerPanel.add(tick); } }
/* 실제 컴포넌트에 값을 설정하면 눈금에 색상을 변경한다 * 값의 범위는 0~100 으로 하자 */ public void setValue(int value) { if (value > 100) { // 100을 넘어가면 100으로 value = 100; } else if (value < 0) { // 0보다 작으면 0으로 value = 0; }
// 일단 전체 tick 을 검정색으로 칠하고 for (int j = 0; j < tickCount; j++) { Tick tick = (Tick)this.centerPanel.getComponent(j); tick.setBackground(Color.black); tick.repaint(); }
// 입력된 value 값에 해당하는 tick들만 다시 녹색으로 칠한다 for (int i = 0; i < tickCount; i++) { Tick tick = (Tick)this.centerPanel.getComponent(i); if (tickCount - i > value / 10) { // nothing to do } else { tick.setColor(Color.green); tick.repaint(); } }
// 하단에 숫자로 값을 표시 this.dataLabel.setText(value + " %"); }
public static void main(String args[]) { JFrame frame = new JFrame(); // 테스트용 프레임을 생성하자 frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); PmTickChartPanel tickPanel = new PmTickChartPanel(); frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(tickPanel, BorderLayout.CENTER);
frame.setSize(300, 300); frame.setVisible(true);
for (int i = 0 ; i < 100 ; i++) { // 테스트를 위해 값을 설정하는 부분 try { Thread.sleep(100); // 잠시 쉬었다가 tickPanel.setValue(i); // 1씩 증가되는 값을 설정, 10 단위로 눈금 색상 변경 } catch (InterruptedException ie) { ie.printStackTrace(); } } } }
/* 10 단위의 눈금 하나에 해당하는 컴포넌트 * 같은 값을 갖는 것이 좌우 하나씩 쌍으로 구성된다 */ class Tick extends JPanel { JPanel lPanel = new JPanel(); JPanel rPanel = new JPanel(); GridLayout layout = new GridLayout(1, 2);
ØApplet 과 Frame 은대표적인최상위컨테이너로써다른컨테이너를포함한맨바깥쪽컨테이너를의미한다.
u컨테이너의사용
컨테이너
설명
Window
컨테이너
최상위 윈도우를 제공하는 컨테이너, Frame Dialog 컨테이너가 주로 대신 사용됨
메소드 이름
기능
void pack()
배치 관리자를 실행시켜서 포함된 컴포넌트의 배치를 시작하고, 윈도우의 크기를 조절함
void show()
윈도우를 화면에 나타나게 하며 윈도우 중에 가장 맨 상단에 나타나게 한다.
void dispose()
다 사용한 윈도우 리소스를 해제시킨다.
Frame
컨테이너
다른 컨테이너와 마찬가지로 add() 메소드를 사용. 배치 요구.
Frame 클래스 생성 후 setSize() 또는 setBounds() 메소드를 이용해서 크기를 꼭 정해줘야 하며 하부 컴포넌트 배치를 위해 pack() 메소드를 호출하고 마지막으로 setVisible(true) 메소드를 통해 화면에 표시해야 한다.
코드 및 예는 표 하부를 참조할 것
Panel
컨테이너
Applet과 Frame 클래스가 가장 바깥쪽의 컨테이너 역할을 하는데 반해, Panel 클래스는 컴포넌트들을 그룹별로 모을 때 주로 사용한다. 보통 GUI를 설계할 때 컴포넌트를 독립적으로 Applet과 Frame에 바로 붙이기 보다는 Panel 클래스에 그룹별로 붙이고 패널 클래스들을 배치하는 경우가 대부분이다.
코드 및 예는 표 하부를 참조할 것
Applet
컨테이너
Applet 클래스는 16장에서 살펴본다.
setSize(), setBounds() 메소드를 해 주어도 원하는 크기대로 웹 브라우저에서 나타나지 않는 경우가 있다. 왜냐면 브라우저의 종류에 따라서 조금씩 차이가 있기 때문에 직접적인 메소드보다는 HTML 태그의 사이즈 부분에 값을 주는 것이 바람직하다.
Dialog
컨테이너
팝업 윈도우의 형태로 메인 윈도우 외에 따로 메시지를 출력하거나, 사용자의 입력을 받을 때 주로 사용되는 컨테이너다.
생성자를 알아보자
형태
기능
Dialog(Frame owner)
Dialog(Dialog owner)
기본 생성자.
Dialog(Dialog owner, String title)
Dialog(Frame owner, String title)
Dialog에 타이틀에 추가
Dialog(Dialog owner, String title, boolean modal)
Dialog(Frame owner, String title, boolean modal)
해당 Dialog를 모달(Modal)로 할 것인지의 여부를 지정함, 모달이란 Dialog가 활성화되어 있을 때는 다른 윈도우를 선택할 수 없는 기능임
Dialog(Frame owner, boolean modal)
상위 컨테이너가 Frame인 경우의 생성자
생성과정
① Dialog 클래스 상속 : 상속을 통해서 개발자가 새로운 대화창을 만든다.
② super() 를 이용하여 생성자 호출
③ setVisible() 메소드 호출
코드 및 사용 예는 표 하부를 참조할 것
ØFrame 컨테이너사용예
import java.awt.*;
publicclass FrameTest {
publicstaticvoid main(String args[]){
Frame f = new Frame("테스트프레임");// 프레임생성
Label l = new Label("5초후에없어지는프레임");
f.add(l);
f.setBounds(10, 10, 300, 200); //크기위치셋팅
f.setVisible(true);// 화면에나타낸다.
try{
Thread.sleep(5*1000);
}catch(InterruptedException e){}
f.setVisible(false);
f.dispose();// 자원을해제한다.
}
}
<실행결과>
ØPanel 컨테이너사용예
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
public class PanelTest extends Applet implements ActionListener{
Panel panel1, panel2;
Button button1, button2, button3, button4;
public void init(){
panel1 = new Panel();
panel2 = new Panel();
panel1.setBackground(Color.red);
panel2.setBackground(Color.yellow);
add(panel1);
add(panel2);
button1 = new Button("패널 2 보이기");
button2 = new Button("패널 2 안보이기");
button3 = new Button("패널 1 보이기");
button4 = new Button("패널 1 안보이기");
button1.addActionListener(this);
button2.addActionListener(this);
button3.addActionListener(this);
button4.addActionListener(this);
panel1.add(button1);
panel1.add(button2);
panel2.add(button3);
panel2.add(button4);
}
// 버튼이눌리면 actionPerformed() 메소드가호출된다.
public void actionPerformed(ActionEvent ae){
Button b = (Button)ae.getSource();
String label = b.getLabel();
if(label.equals("패널 1 보이기"))
panel1.setVisible(true);
else if(label.equals("패널 1 안보이기"))
panel1.setVisible(false);
else if(label.equals("패널 2 보이기"))
panel2.setVisible(true);
else
panel2.setVisible(false);
}
}
<실행결과>
ØDialog 컨테이너사용예
<Notification.java>
import java.awt.*;
import java.awt.event.*;
class Notification extends Dialog implements ActionListener{
String msg;
public Notification(Frame f, String s){
super(f,"주목",true);
msg = s;
}
publicvoid disp(){
Button b;
add("North", new Label(msg,Label.CENTER));
b = new Button("OK");
b.addActionListener(this);
Panel p = new Panel();
p.add(b);
add("South", p);
setBackground(Color.gray);
setSize(160,100);
setVisible(true);
}
publicvoid actionPerformed(ActionEvent e){
dispose();
}
}
<TestDialog.java>
import java.awt.*;
import java.awt.event.*;
class TestDialog extends Frame implements ActionListener{
└ 최상의 컨네이너 구조(JWindow, JDialog, JApplet, JFrame)이 있는데 각각의 AWT의 Window,Dialog,Frame을 상속받아서 구현한 컨테이너이다.
└ 최상위 컨테이너는 Swing's GUI Application Program을 할 때 최소한 한개 이상을 가지고 있다.
└ 최상위 컨테이너는 컴포넌트를 직접적으로 추가하지 않고, 포넌트의 추가/삭제를 JRootPane클래스에 위임하는 구조를 갖는다.
- layerdPane에는 ContentPane하고 GlassPane이 있고, LayerPane은 RootPane에 붙고 RootPane은 Frame에 붙는다.
awt는 바로 frameㅇ을 content를 붙이지만 swing에서는 JFrame에 JComponent를 추가 할 수 없다.
왜냐하면 컴포넌트의 추가/삭제는 JRootPane에 위임하기 때문이다.
따라서 대부분의 컴포넌트는 ContentPane에 추가/삭제하도록 되어있다.
⊙ setSize(400,500);
└ JFrame에 width와 height를 지정한다. -setSize는 프레임의 크기
⊙ pack();
└ pack() 컴포넌트 크기만큼 JFrame의 사이즈를 구성한다.
⊙ setLocation(500,500);
└ JFrame에 시작 위치(width,height)를 지정.
⊙ setBounds(500,500,400,500);
└ 크기와 위치를 동시에 지정 -setBounds(setSize,setLocation);
⊙ setVisible(true);
└ JFrame을 보여주는지를 지정. true=>보여준다 / false=>보여주지 않는다.
⊙ setDefaultCloseOperation =>JFrame 종료 할때 어떤 동작할 것인가를 지정
<옵션>
└ DO_NOTHING_ON_CLOSE : 종료를 눌렀을때 아무 동작도 하지 말아라.
└ HIDE_ON_CLOSE (default) :프로그램 종료 하지 않은 상태에서 숨어있어라.
└ DIPOSE_ON_CLOSE : 현재프레임만 종료하라.
└ EXIT_ON_CLOSE : 모든 프레임을 종료하라.
⊙ setResizable(true);
└ 프레임의 크기를 고정할 것인가 를 지정. true=>사이즈를 변경 가능. false=> 사이즈 변경 불가능.
⊙ JFrame.setDefaultLookAndFeelDecorated(true);
└ JFrame을 JAVA LookAndFeel로 바꿔주는 메소드. 만약 false일경우 자바로 변경이 안됨.
주의) JFrame 객체가 생성되기 이전에 호출되어야 한다.
ex> public static void main(String[] args){
JFrame.setDefaultLookAndFeelDecorated(true);
new JFrameDemo("프레임 테스트");
}
import javax.swing.*; import java.awt.*; public class NoTitleBarFrameTest { public static void main(String[] args) { JFrame f = new JFrame(); f.setUndecorated(true); f.setSize(300,300); f.setVisible(true); //JWindow w = new JWindow(); //w.setSize(300,300); //w.setVisible(true); } }
setUndecorated 메소드를 사용해 보세요. 주의점을 setVisible(true) 전에 해야 한다는 것입니다.
다운받은 파일의 압축을 풀고 환경변수 및 패스를 잡아줍니다 set ANT_HOME=c:\ant set JAVA_HOME=c:\jdk1.4.2 set PATH=%PATH%;%ANT_HOME%\bin
III. 간단한 Ant 예제
Ant를 이용하여 web application을 구성할 때 다음의 구조를 유지하기를 권장합니다
① build : src, web, docs에서 결과적으로 만들어진 산출물 디렉토리 ② dist : build를 배포하기 위한 배포 디렉토리 ③ docs : 배포판에 배포할 정적인 문서를 관리할 디렉토리 ④ src : /WEB-INF/classes 에 위치할 java 소스 디렉토리 ⑤ web : HTML, JSP, 이미지등의 컨텐트 디렉토리 (WEB-INF의 서브디렉토리 포함) ⑥ build.properties : build.xml에서 사용할 properties ⑦ build.xml : ant 명령으로 실행될 설정파일
src에 하나이상의 java 소스를 테스트로 넣어 놓으세요
자 이렇게 디렉토리를 설정하고 build.xml 을 다음 step에 따라 따라 해 BOA요 ^^&
STEP 1. build.xml 의 기본구조
xml을 기본적인 내용을 안다면 이해하기 쉽습니다
하나의 build 파일은 하나의 project로 구성되며 이는 다시 여러 target으로 구성됩니다
target 이란 빌드 과정중 수행해야 할 task들을 모아놓은 job 단위 라고 보면 됩니다
compile target이라 한다면 compile에 관련된 작업들을 모아놓은 그룹이라 생각하면 쉽게 이해 될겁니다
STEP 2. 시~작 Ant 맛보기~ ① build.xml에 다음을 입력한 후 저장 합니다
-. project
project는 하나 이상의 target을 정의 합니다 또한 하나의 target은 task의 집합입니다
ant를 실행할 시에 어느 타겟을 실행할 것인지 지정할 수가 있으며 (예: \ant clear)
지정하지 않았을 경우 디폴트로 설정된 값이 사용됩니다 이부분이 default="clear"입니다
-. property
전역변수 설정 혹은 그렇게 사용할 build.properties를 정의 합니다
build.properties에 catalina.home을 정의하였으며 여러 환경이 변하더라도 이 값만
변경해주면 build.xml을 수정없이 바로 실행 가능합니다
-. echo
message 내용을 출력 합니다
-. target
target 이란 task의 집합으로 실질적으로 실행될 코드들의 묶음입니다
여기서는 아무 task도 없습니다
② build.properties에 다음을 입력 후 저장합니다
catalina.home 은 변수로 사용할 것이며 그 값은 C:\Tomcat 5.0입니다
③ 실행
해당 디렉토리로 이동하여 도스창에서 ant 라고 칩니다
STEP 3. 사전작업 하기~
이번 단계에서는 컴파일 하기전 전역변수 선언이나 컴파일 시 클래스 패스 설정을 해봅시다
① build.xml
-. project
이번에는 default 값을 prepare로 하였습니다 고로 target은 prepare가 실행될 것입니다
-. property
역시나 build.properties를 정의하였고 여러 전역변수를 설정하였습니다
build.home 이란 변수에는 ${basedir}/build 값이 정의되었으며
build.home은 ${build.home}으로 사용할수 있습니다
궁금하면 <echo message="${build.home}"/> 등으로 출력해 봅시다~
-. path
${catalina.home} 은 build.properties에서 정의하였다는것을 기역하실겁니다
fileset은 파일들의 집합을 나타내는데 어떤 특정파일만 포함 할수 있거나 혹은 어느 특정파일만 제외할 수 있습니다
javadoc target의 javadoc 태스트를 보면 java 소스가 있는 소스디렉토리와
API를 생성할 타겟 디렉토리를 정해주면 알아서 API를 생성해 줍니다
만들어진 API는 배포버젼의 dist디렉토리로 해주면 더 좋겠지요
dist target은 배포파일인 war를 만듭니다
필요한 문서가 있으면 docs 디렉토리를 만들어 로 복사도 하도록 합시다
jar 태스크는 위의 방식과 같이 사용합니다
② 실행
VI. Ant 실행
① C:\예제\ant -help
ant [options] [target [target2 [target3] ...]]
Options : -help 이 메세지의 표시 -projecthelp 프로젝트 도움 정보의 출력 -version 버전 정보의 출력과 종료 -diagnostics diagnose 나 report 문제에 도움이 되는 정보의 출력. -quiet, -q 한층 더 메세지를 적게 -verbose, -v 한층 더 메세지를 많게 -debug 디버그 정보의 출력 -emacs adornments 없이 로그 정보의 생성(produce) -logfile <file> 로그를 지정 파일에 출력 -l <file> '' -logger <classname> 로그 생성을 실행하기 위한 클래스 -listener <classname> 프로젝트 청취자(listener) 역할의 class의 인스턴스를 추가 -buildfile <file> 지정된 빌드 파일의 사용 -file <file> '' -f <file> '' -D<property>=<value> 지정된 프로퍼티의 값의 사용 -propertyfile <name> 모든 프로퍼티를 파일로부터 로드 (-D프로퍼티보다 전에) -inputhandler <class> 입력 요청(requests)를 취급하는 클래스 -find <file> 파일시스템의 루트로 향해 빌드파일을 검색하고 그것을 사용
② C:\예제\ant
현재 디렉토리에 있는 build.xml 파일을 이용해, 디폴트 타겟으로 Ant 를 실행합니다.
③ C:\예제\ant compile
현재 디렉토리에 있는 build.xml이 실행되며 파라미터로 compile을 지정하면 project의 default 값을 무시하고 compile target을 실행합니다 물론 depends 가 있다면 먼저 실행합니다
④ C:\예제\ant -buildfile test.xml
현재 디렉토리에 있는 test.xml 파일을 이용해, 디폴트 타겟으로 Ant 를 실행합니다.
⑤ C:\예제\ant -buildfile test.xml dist
현재 디렉토리에 있는 test.xml 파일을 이용해, dist 라는 이름의 타겟으로 Ant 를 실행합니다.