태원 Wedding Day
지금쯤이면 달콤한 신혼의 밤을 보내고 있겠군..
오랜 시간동안 만나왔던 두 사람이니 만큼 행복하게 잘 살아야해..
원서로 두권정도 출간된 적이 있는데요..
그게 다 넷빈즈 3.x대 서적이라 지금은 거의 도움이 안될겁니다.
지금 나와있는 서적은 저도 본적이 없구요..
지금은 netbeans.org에서 정보를 얻으시는게 가장 빠른길인듯 합니다.
이전 포스팅에서도 잠깐 언급했던 설치형 갤러리인 Gallery2 가
3월17일자로 근 일여년만에 2.1.2에서 2.2로 업버전을 릴리즈 했다.
사이트에서 소개하고 있는 Gallery2의 새로운 기능은
New Features of Gallery 2.2 Include:
...and many more!
란다..
와우~ 바로 업그레이드를 단행했다..
2.1.2버전까지 지원되지 않아 많은 갤러리2 유저가 원했던 일괄 워터마크 적용
기능이 가장 마음에 든다.
업그레이드를 단행 하면서 테마도 럭셔리한 PGtheme로 교체하고..
몇가지 손을 더 봐야 하지만... 좀 느긋이 진행 해야겠다..
저도 최근에 2.2로 업그레이드를 단행했습니다.
gallery2 짱이죠...^^
저 같은 경우엔 gallery를 쓰는 이유가.
.사진을 쉽게 온라인으로 올릴 수 있다는 점
.사진을 일정 크기로 자동 변환해준다는 점.
.사진의 링크 주소를 쉽게 만들어 준다는 점.
(이건 안 쓰시는것 같던데.. URL Rewrite 한번 사용해 보세요. 주소를 http://photo.nanbean.net/1616-1/300.jpg 이런식으로 만들어줍니다.)
입니다.
정말 잘만든 프로그램 같습니다.
조금 느리다는게 조금 걸리는데..
온라인으로 사진을 관리하는데는 이만한 프로그램이 없는것 같습니다.^^
kldp쪽 보니 Gallery2에대한 얘기가 심심찮게 올라오더라구요..
저역시 Rewrite 모듈을 활성화 할까 만까로 약간 고민 중이랍니다. ^^
오호~ 멋진데요?^^
초면에 무리한 부탁인것 같기도 하지만,
저도 혹시 소스 좀 얻을 수 있을까요?
혹시 가능하다면 nanbean앳nanbean점net으로 보내주시면 감사히 쓰겠습니다..^^
일단 플러그인으로 제작은 했는데..
제 환경에 맞춰 만들다보니
옵션으로 설정을 할 수 있는 부분이 없습니다.
몇가지 옵션을 적용 해서 태터 플러그인으로 공개 하겠습니다.
예.. 사내 사진 동호회가 있어서 한달에 한번 출사를 나갑니다.(늘 모여 놀기만 하지만요..^^)
저도 얼른 저의 '그분'을 찾아야 겠습니다.
Java API for XML Web Services (JAX-WS) 2.0, JSR 224, is an important part of the Java EE 5 platform. A follow-on release of Java API for XML-based RPC 1.1(JAX-RPC), JAX-WS simplifies the task of developing web services using Java technology. It addresses some of the issues in JAX-RPC 1.1 by providing support for multiple protocols such as SOAP 1.1, SOAP 1.2, XML, and by providing a facility for supporting additional protocols along with HTTP. JAX-WS uses JAXB 2.0 for data binding and supports customizations to control generated service endpoint interfaces. With its support for annotations, JAX-WS simplifies web service development and reduces the size of runtime JAR files.
This document takes you through the basics of using the IDE to develop a JAX-WS web service and to consume it in three different clients—either a Java class in a Java SE application, or a servlet or JSP page in a web application. The three clients that you create in this document are separate applications, all consuming the same web service.
Expected duration: 25 minutes
Before you begin, you need to install the following software on your computer:
If you have not registered an instance of the Sun Java System Application Server 9.0, you must do so before you can begin developing Java EE 5 applications:
Note: If you want to deploy to the Tomcat Web Server, you can do so, except that, since it only has a web container, you should create a web application, not an EJB module, in the next section. JAX-WS web services, unlike JSR-109 web services, can deploy successfully to the Tomcat web container.
The goal of this exercise is to create a project appropriate to the deployment container that you decide to use. Once you have a project, you will create a web service in it.
You can either deploy your web service in a web container or in an EJB container. This depends on implementation choices. For example, if you plan to deploy to the Tomcat Web Server, which only has a web container, you should choose to create a web application, and not an EJB module.
The Projects window displays the new web service. For example, for web applications the Projects window now looks as follows:
The IDE automatically creates the deployment descriptors required for the server, if any. For the Sun Java System Application Server, no deployment descriptor is needed. For web services deployed to the Tomcat Web Server, sun-jaxws.xml and a WSServlet item in web.xml are added.
In this exercise, you created a NetBeans project and set up the web service.
The goal of this exercise is to do something meaningful with the files and code that the IDE has generated for you. You will add an operation that will add two numbers received from a client.
@WebMethod public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) { // TODO implement operation return 0; }
@WebMethod public int add(@WebParam(name = "i") int i, @WebParam(name = "j") int j) { int k = i + j; return k; }
In this exercise, you added code to the web service.
When you deploy a web service to a web container, the IDE lets you test the web service to see if it functions as you expect. The Tester application, provided by the Sun Java System Application Server, is integrated into the IDE for this purpose. For the Tomcat Web Server, there is a similar tool. However, while the Sun Java System Application Server's Tester page lets you enter values and test them, the Tomcat Web Server does not. In the latter case, you can only see that the web service is deployed, you cannot test the values. No facility for testing whether an EJB module is deployed successfully is currently available.
To test successful deployment to a web container:
Note: Since the result of a deployed EJB module is not displayed in a browser, you cannot take the step above if you are working with an EJB module.
The IDE starts the application server, builds the application, and opens the tester page in your browser, if you deployed a web application to the Sun Java System Application Server. For the Tomcat Web Server and deployment of EJB modules, the situation is different:
Deployment of application CalculatorWSApplication completed successfully Enable of CalculatorWSApplication in target server completed successfully Enable of application in all targets completed successfully All operations completed successfully run-deploy: run: BUILD SUCCESSFUL
The sum of the two numbers is displayed:
In this exercise, you deployed a web service and tested it.
Now that we have deployed our web service, we need to create a client to make use of the web service's add method. Here, we create three clients— a Java class in a Java SE application, a servlet, and a JSP page in a web application.
Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".
Click Finish.The Projects window displays the new web service client:
System.out.println("Sum: " + port.add(3,4));
The Output window should now show the following:
compile: run: Sum: 7 BUILD SUCCESSFUL (total time: 1 second)
Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".
Click Finish.
The Projects window displays the new web service client:
/* TODO output your page here
Next, delete the line that ends the section of commented out code:
*/
Add some empty lines after this line:
out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>");
Now, right-click in one of the empty lines that you added. Choose Web Service Client Resources > Call Web Service Operation. The Select Operation to Invoke dialog box appears.
The processRequest method now looks as follows (the added code is in bold below):
protected void processRequest(HttpServletRequest request, HttpServletResponse response) response.setContentType("text/html;charset=UTF-8"); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet ClientServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Servlet ClientServlet at " + request.getContextPath () + "</h1>"); try { // Call Web Service Operation org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService(); org.me.calculator.client.CalculatorWS port = service.getCalculatorWSPort(); // TODO initialize WS operation arguments here int arg0 = 0; int arg1 = 0; // TODO process result here int result = port.add(arg0, arg1); // TODO handle custom exceptions here } out.println("</body>"); out.println("</html>"); out.close(); }
Change the value for arg0 and arg1 to other numbers, such as 3 and 4.
Change the System.out.println statement to out.println.
Add a line that prints out an exception, if an exception is thrown.
The try/catch block should now look as follows (new and changed lines are highlighted):
try { // Call Web Service Operation org.me.calculator.client.CalculatorWSService service = new org.me.calculator.client.CalculatorWSService(); org.me.calculator.client.CalculatorWSApplication port = service.getCalculatorWSApplicationPort(); // TODO initialize WS operation arguments here int arg0 = 3; int arg1 = 4; // TODO process result here int result = port.add(arg0, arg1); out.println("<p>Result: " + result); out.println("<p>Exception: " + ex); }
The server starts, if it wasn't running already; the application is built and deployed, and the browser opens, displaying the calculation result.
Note: At the time of writing, issue Issue 10 was unresolved; therefore, you must create your web service client in a project folder that does not contain spaces in its path. For example, the path should not be "C:\Documents and Settings\...".
Click Finish.
The Projects window displays the new web service client.
Change the value for arg0 and arg1 to other numbers, such as 3 and 4.
The server starts, if it wasn't running already; the application is built and deployed, and the browser opens, displaying the calculation result:
You will be creating a simple SOA project. You will add a WSDL file to your BPEL Module project. You use the Partner view to add the messages, partner link type, port type, and operation. Then you will create a composite application project and use the CASA editor to modify the project configuration. This tutorial also illustrates a basic scenario of how a File Binding Component can be used in a composite application.
Before you begin, you must install the following software on your computer:
Before you can deploy your application, the Sun Java System Application Server and JBI runtime must be configured correctly and running.
To configure the tutorial environment:
In this section, you create a BPEL Module project called HelloSample .
To create the HelloSample project:
In this section, you add a WSDL file to your BPEL Module project and then use Partner view to configure the components of the WSDL Document.
To create HelloSample.wsdl :
To add Messages :
To add PartnerLinkType :
To create HelloSample.bpel :
To add a partner link:
To add a Receive activity:
To add a Reply activity:
To add an Assign activity:
Before you deploy the BPEL Module project, you must add the JBI module to the deployment project. Deploying the project makes the service assembly available to the application server, thus allowing its service units to be run.
To create the Composite Application project:
To use the CASA editor to modify the project configuration:
To create the WSDL endpoint:
To createa new connection:
To Check auto-generated WSDL endpoint properties
To deploy the Composite Application:
To test the SampleCompositeApp:
@http://blogs.sun.com/barkodar/resource/HelloSample.html
Server.xml 보기
J2SE 5.0 출시에는 새로운 top-level Queue
인터페이스가 Collections Framework에 추가되어 Map
, List
, Set
인터페이스와 어우러진다. 일반적으로 queue는 먼저 들어온 것이 먼저 나가는 데이터 스트럭쳐이지만 priority queue같은 몇몇 구현들에서는 queue 뒤에 엘리먼트가 첨부되지 않는다. 이 queue와 FIFO(first in, first out)구조에 관한 얘기는 은행에서의 대기선에 비유할 수 있겠다. 은행직원은 고객이 대기선에 있는지 확인한 후 대기선의 첫번째 손님을 맞이하여 손님이 원하는 거래를 처리한다. 그리고 그 손님은 이제 대기선에서 제외된다.
J2SE 5.0에는 Queue
인터페이스와 함께 몇 가지 새로운 queue 구현이 있다. DelayQueue
가 그 중 하나인데, DelayQueue
에서 queue 안의 아이템들은 지연시간 동안 처리되지 않는다. 이번 테크팁에서는 새로운 Queue
인터페이스와 DelayQueue
구현에 대해 알아보도록 하자.
첫번째로 Queue
인터페이스를 분석해보자. 이 인터페이스는 Collection
을 확장하고 다섯개의 고유 메서드들을 추가한다.
E element()
boolean offer(E o)
E peek()
E poll()
E remove()
J2SE 5.0이 새롭게 generics를 지원하므로 여기에서의 E
는 어느 타입이든지 가능하며, Queue
가 생성될 때 정의된 엘리먼트 타입으로 결정된다.
Queue
의 엘리먼트를 추가하고 제거하는 데 당연히 Collection
인터페이스 메서드들을 사용할 수도 있지만, 그 메서드들은 동작할 때 부가적인 요구사항을 가지므로, Queue
인터페이스에서는 사용하지 않을 것을 권장하고 있다. 예를 들어 Collection.add
메서드로 queue에 엘리먼트를 추가하는 대신에 offer
메서드로 queue에 엘리먼트를 추가할 수 있다. 이 둘은 무슨 차이가 있을까? add 실행이 오류가 날 수 있다. 그 한 예는 queue에 사이즈 제한이 있을 때이다.(은행 대기선에 비유하자면, 10명만 대기 가능할 경우) Collection
의 add
메서드를 사용하면 add
는 예외를 던지면서 실패한다. 이와 비교할 때 offer
메서드는 false를 리턴하며 "실패"하게 된다. 따라서 offer
메서드를 사용하면 실제로 예외적인 상황에서만(특히 체크 안 된 런타임 예외가 던져졌을 경우) 예외처리(exception handling)을 사용하게 된다.
Queue
의 다른 네가지 메서드는 두개씩 짝지어 설명할 수 있다. remove
/poll
, element
/peek
. remove
와 poll
메서드는 둘 다 queue의 첫번째 엘리먼트 즉 "head"를 제거하는 데 사용된다. 빈 Collection 객체에서 호출되었을 때, remove
메서드는 예외를 던지고, poll
메서드는 단순히 null 값을 리턴한다. head 엘리먼트를 제거하는 대신 단지 그 엘리먼트를 살펴볼 수도 있다. 이 때 element와 peek 메서드가 사용된다. 여기서 element
메서드는 빈 queue에서는 예외를 던지고, peek
은 null값을 리턴한다. Queue는 일반적으로 태스크를 진행하는 데 사용되므로 빈 queue를 갖는 것이 예외상황일 필요는 없다. 따라서, poll
/peek
모델이 사용하기에 보다 적합할 것이다. (앞에서 본 메서드들 중 예외를 안 던지고 null 리턴하는 메서드들)
Queue
는 다음과 같은 상황에서 일반적으로 사용된다.
class Producer implements Runnable { private final Queue queue; Producer(Queue q) { queue = q; } public void run() { try { while(true) { queue.offer(produce()); } } catch (InterruptedException ex) { ... handle ...} } Object produce() { ... } } class Consumer implements Runnable { private final Queue queue; Consumer(Queue q) { queue = q; } public void run() { try { Object o; while((o = queue.poll()) != null) { consume(o); } } catch (InterruptedException ex) { ... handle ...} } void consume(Object x) { ... } }
Queue가 꽉 차 있거나(생산자의 입장에서) 비어있을 때(소비자의 입장에서) 어떤 일이 일어나는 지 궁금할 것이다. 이 시점에서 새로운 queue 인터페이스 BlockingQueue
를 설명하는 것이 좋겠다. Queue
를 사용하여 엘리먼트를 무작정 추가하거나(offer사용) 삭제하는(poll
사용) 대신 BlockingQueue
의 put
메서드로 엘리먼트를 추가하고 take
메서드로 제거할 수 있다. put
과 take
는 모두 이를 호출한 쓰레드가, 특정 조건 하에서 블로킹되게끔 한다. put
은 queue가 꽉 차 있을 경우, take
는 queue가 비어있을 경우가 블로킹 조건이다.
BlockingQueue
의 일반적인 사용패턴은 다음과 같다.
class Producer implements Runnable { private final BlockingQueue queue; Producer(BlockingQueue q) { queue = q; } public void run() { try { while(true) { queue.put(produce()); } } catch (InterruptedException ex) { ... handle ...} } Object produce() { ... } } class Consumer implements Runnable { private final BlockingQueue queue; Consumer(BlockingQueue q) { queue = q; } public void run() { try { while(true) { consume(queue.take()); } } catch (InterruptedException ex) { ... handle ...} } void consume(Object x) { ... } }
각 생성된 생산자가 꽉 찬 queue에 새로운 아이템을 추가하려 하면 put
메서드에서 기다리고, take
메서드에서는 꺼내갈 것이 추가될 때까지 기다린다. queue가 비어있다면 while(true)
조건문에는 아무런 변화도 일어나지 않을 것이다.
DelayQueue는 BlockingQueue 인터페이스의 구체적인 구현이다. DelayQueue 에 추가된 아이템들은 반드시 새로운 Delayed 인터페이스를 구현해야하며, 이 Delayed는 한 개의 메서드, long getDelay(TimeUnit unit)를 갖고 있다. DelayQueue는 우선순위 힙 데이터 스트럭쳐에 기반한 시간 기준 스케쥴링 Queue로 동작한다
데모를 위해, 다음의 프로그램 DelayTest
는 몇 초안에 실행되는 Delayed 인터페이스를 구현한다. 알아둬야할 것은1) nanosecond는 10억분의 1초, 2) nanosecond 유니트에서 작업을 가능케하는 System의 새로운 메서드, nanoTime 이 있다는 것이다. getDelay 메서드가 nanosecond로 리턴된 횟수를 필요로 하기 때문에 nanosecond에서 작업하는 것이 중요하다.
import java.util.Random; import java.util.concurrent.Delayed; import java.util.concurrent.DelayQueue; import java.util.concurrent.TimeUnit; public class DelayTest { public static long BILLION = 1000000000; static class SecondsDelayed implements Delayed { long trigger; String name; SecondsDelayed(String name, long i) { this.name = name; trigger = System.nanoTime() + (i * BILLION); } public int compareTo(Delayed d) { long i = trigger; long j = ((SecondsDelayed)d).trigger; int returnValue; if (i < j) { returnValue = -1; } else if (i > j) { returnValue = 1; } else { returnValue = 0; } return returnValue; } public boolean equals(Object other) { return ((SecondsDelayed)other).trigger == trigger; } public long getDelay(TimeUnit unit) { long n = trigger - System.nanoTime(); return unit.convert(n, TimeUnit.NANOSECONDS); } public long getTriggerTime() { return trigger; } public String getName() { return name; } public String toString() { return name + " / " + String.valueOf(trigger); } } public static void main(String args[]) throws InterruptedException { Random random = new Random(); DelayQueue<SecondsDelayed> queue = new DelayQueue<SecondsDelayed>(); for (int i=0; i < 10; i++) { int delay = random.nextInt(10); System.out.println("Delaying: " + delay + " for loop " + i); queue.add(new SecondsDelayed("loop " + i, delay)); } long last = 0; for (int i=0; i < 10; i++) { SecondsDelayed delay = (SecondsDelayed)(queue.take()); String name = delay.getName(); long tt = delay.getTriggerTime(); if (i != 0) { System.out.println("Delta: " + (tt - last) / (double)BILLION); } System.out.println(name + " / Trigger time: " + tt); last = tt; } } }
DelayTest
프로그램은 엘리먼트를 실행하기 전 DelayQueue
안에 그 10개의 엘리먼트를 위치시킨다.
다음은 DelayQueue
를 한번 구동했을 때의 출력물이다.
Delaying: 8 for loop 0 Delaying: 7 for loop 1 Delaying: 2 for loop 2 Delaying: 4 for loop 3 Delaying: 0 for loop 4 Delaying: 9 for loop 5 Delaying: 3 for loop 6 Delaying: 4 for loop 7 Delaying: 6 for loop 8 Delaying: 2 for loop 9 loop 4 / Trigger time: 1883173869520000 Delta: 1.9995545 loop 2 / Trigger time: 1883175869074500 Delta: 0.0012475 loop 9 / Trigger time: 1883175870322000 Delta: 0.9995177 loop 6 / Trigger time: 1883176869839700 Delta: 0.9995187 loop 3 / Trigger time: 1883177869358400 Delta: 6.408E-4 loop 7 / Trigger time: 1883177869999200 Delta: 2.0001667 loop 8 / Trigger time: 1883179870165900 Delta: 0.9986953 loop 1 / Trigger time: 1883180868861200 Delta: 0.9995595 loop 0 / Trigger time: 1883181868420700 Delta: 1.001262 loop 5 / Trigger time: 1883182869682700
이 출력물은 loop4를 위한 아이템이 time 0, 즉 지연 없이 시작하기로 설정되어있다는 것을 가리키고 있으며,
Delaying: 0 for loop 4
따라서 첫번째로 다음과 같이 구동한다.
loop 4 / Trigger time: 1883173869520000
Loop 2에서는 2초간의 지연이 있으며,
Delaying: 2 for loop 2
따라서 다음과 같이 나타난다.
loop 2 / Trigger time: 1883175869074500
여기서의 delta는 1.9995545로 약 2초이다.
Delta: 1.9995545
다른 loop를 위한 비슷한 delta들도 존재한다.
좀 더 실제적인 예를 위해 DelayQueue
에서 pull된 것을 단지 출력하는 대신, queue의 아이템들을 Runnable
구현시키고, 그 아이템들의 run
메서드를 호출할 수 있다.
Queue
, DelayQueue
와 J2SE 5.0에서 바뀐 다른 Collections Framework에 대한 좀 더 많은 정보를 원한다면 Collection Framework Enhancements를 참조하기 바란다.
@http://kr.sun.com/developers/techtips/c2004_1019.html
이 글은 HttpURLConnection 과 이의 서브클래스인 HttpsURLConnection 을 사용하여 보안 웹 페이지에 액세스하는 방법을 보여준다. 또한 비보안 페이지(non-secure page)에서 보안 페이지(secure one)로의 리다이렉트를 쉽게 할 수 있는 방법도 볼 수 있다. HTTP 와 HTTPS에 관한 정보는 HTTP 1.1 RFC 2616과 HTTPS RFC 2818를 참고하기 바란다.
첫번째 예로, 주어진 URL에 접속하기 위해 다음 WebPageReader 프로그램의 HttpURLConnection 를 이용해 보자. 그리고 페이지의 내용을 스탠다드 아웃(standard out)에 출력하자.
[code] import java.net.URL; import java.net.MalformedURLException; import java.net.URLConnection; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; public class WebPageReader { private static URLConnection connection; private static void connect( String urlString ) { try { URL url = new URL(urlString); connection = url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "50000"); } catch (MalformedURLException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static void readContents() { BufferedReader in = null; try { in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ( (inputLine = in.readLine()) != null) { System.out.println(inputLine); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); readContents(); } } [/code]만약 현재의 위치가 방화벽 뒤라면, 다음과 같이 설정된 proxyHost과 proxyPort 변수들이 필요하다.
[code] http.proxyHost=webcache http.proxyPort=8080 https.proxyHost=webcache https.proxyPort=8080 [/code]커맨드 라인에 -D플래그를 이용해서 값을 직접 입력하거나 프로그램 상에서 System.setProperty()를 호출함으로써 변수를 구할 수있다. WebPageReader 를 컴파일한 후에 이하와 같은 커맨드로 Core Java Technologies Tech Tips 홈 페이지의 내용을 열거해 볼 수 있다.
[code] java WebPageReader http://java.sun.com/developer/JDCTechTips/ [/code]URLConnection는 추상 클래스이다. URL클래스의 openConnection() 메소드는 지정된 URL을 읽을 수 있도록 적절하고 구체적인 서브클래스를 리턴한다. http나 https URL을 입력하면 이는 HttpURLConnection나 HttpsURLConnection의 서브클래스가 될 것이다. 만약 다음을 openConnection()를 호출하는 라인에 추가하면,
[code] System.out.println(connection.getClass()); [/code]HttpURLConnection의 숨겨진 구현 클래스의 인스턴스를 리턴하는 커넥션(connection)을 보게 된다. 예를 들면, 다음과 같다.
[code] class sun.net.www.protocol.http.HttpURLConnection [/code]이와 유사하게 보안 페이지를 읽기 위해 동일한 WebPageReader코드를 사용할 수가 있다.
[code] java WebPageReader https://today.dev.java.net [/code]후자의 경우, 커넥션은 HttpsURLConnection의 서브클래스인 HttpURLConnection타입이라는 것을 알아야 한다. 보다 명확하게 말하자면 다음과 같은 숨어있는 구현 클래스가 있다는 것을 인식할 수 있어야 한다.
[code] class sun.net.www.protocol.https.HttpsURLConnectionImpl [/code]일반적으로 브라우저에 URL을 입력할 때, 이동하고자하는 페이지가 보안 페이지인지 아닌지는 알 수가 없다. 다시 말하면, today.dev.java.net 페이지를 보기 위해서는 http://today.dev.java.net를 입력한다. 필요하다면 브라우저가 리다이렉트할 것이고 사용자를 https://today.dev.java.net로 연결하기 위해 적절한 신호변경을 수행한다. WebPageReader프로그램이 요청된 리다이렉션을 수행하는지를 살펴보자.
[code] java WebPageReader http://today.dev.java.net [/code]원하는 페이지로 리다이렉트되는 대신에 다음과 같은 메시지를 보게 된다.
[code] <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <HTML><HEAD> <TITLE>301 Moved Permanently</TITLE> </HEAD><BODY> <H1>Moved Permanently</H1> The document has moved <A HREF="https://today.dev.java.net/">here</A>.<P> <HR> <ADDRESS> Apache/1.3.26 Server at today.dev.java.net Port 80 </ADDRESS> </BODY></HTML> [/code]이 정보로부터 무언가를 읽어내는 것은 어렵지만 문제는 리다이렉션에 관한 것이 아니다. URL http://linux.java.net으로 프로그램을 실행하면, 프로그램은 http://community.java.net/linux으로 적절히 이를 리다이렉트하고 사용자는 원하는 컨텐츠를 볼 수가 있다. 어떤 일이 일어나는지를 자세히 살펴보려면 HttpURLConnection를 명시적으로 이용해야 한다. 스탠다드 아웃에 웹 페이지의 컨텐츠를 출력하는 코드를 삭제해서 일을 간단히 해보자. RedirectingReader프로그램이다.
[code] import java.net.URL; import java.net.MalformedURLException; import java.net.HttpURLConnection; import java.io.IOException; public class RedirectingReader { private static HttpURLConnection connection; private static void connect( String urlString ) { try { URL url = new URL(urlString); connection = (HttpURLConnection)url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "30000"); System.out.println(connection.getURL()); System.out.println( connection.getResponseCode() + " " + connection.getResponseMessage()); System.out.println(connection.getURL()); } catch (MalformedURLException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println( "usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); } } [/code]다음과 같이 리다이렉트하는 URL을 입력하여 RedirectingReader 를 컴파일하고 실행해 보자.
[code] java RedirectingReader http://linux.java.net/ [/code]다음과 같은 출력값을 보게 될 것이다.
[code] http://linux.java.net/ 200 OK http://community.java.net/linux/ [/code]첫번째 라인이 재빨리 나타난다. String값으로 URL을 생성하고 HttpURLConnection객체를 실행하자. 그러면 http://linux.java.net를 요청하는 동안에 잠깐의 멈춤이 생긴다. 페이지가 리다이렉트되는 이 시간동안 받게 되는 응답 코드와 메시지는 리다이렉트된 페이지로부터 온 것이다. 이 리다이렉트된 페이지는 출력값의 3번째 라인에 열거된다. URL url = new URL(urlString);아래의 라인을 추가해서 리다이렉션을 허용하지 않았을 때 발생하는 현상을 보자.
[code] HttpURLConnection.setFollowRedirects(false); [/code]출력값은 다음과 같다.
[code] http://linux.java.net/ 302 Found http://linux.java.net/ [/code]이는 302 에러가 발생했다는 것을 나타낸다. 곧 리다이렉트되지 않고 URL이 그대로 유지되었다는 것이다. 이를 처리하기 전에, 좀 전에 추가했던 라인을 지우고 리다이렉션이 다시 작동하는지를 확인하기 위해 RedirectingReader 프로그램을 실행시켜보자. RedirectingReader를 다시 한번 실행시키고, http://today.dev.java.net 를 입력하자. 다음과 같은 출력값을 보게 된다.
[code] http://today.dev.java.net 301 Moved Permanently http://today.dev.java.net [/code]위는 이 프로그램이 "Moved Permanently" 에러를 처리할 수 없어서 리다이렉트 했다는 것을 말한다. 이것이 바로 원했던 디폴트 속성이다. 보안문제 때문에 http 과 https간에는 리다이렉트할 수 없다. 어디로 리다이렉트할 것이냐하는 정보는 이 응답의 헤더 부분에 있다. 이전의 요청에 대한 전체 응답 헤더는 다음과 같다.
[code] HTTP/1.1 301 Moved Permanently Date: Tue, 03 Feb 2004 01:38:43 GMT Server: Apache/1.3.26 (Unix) mod_ssl/2.8.10 OpenSSL/0.9.6b mod_jk/1.2.1 Location: https://today.dev.java.net/ Content-type: text/html; charset=iso-8859-1 [/code]HttpURLConnection 내의 getResponseCode() 과 getResponseMessage() 메소드가 이 응답의 첫번째 라인에 포함된 정보를 리턴하는 것을 본 적이 있을 것이다. 혹은 getHeaderField() 메소드를 이용해서 헤더 필드의 이름에 스트링값을 넣어주어도 된다. 가령, getHeaderField("Location")는 https://today.dev.java.net/값을 리턴한다.
이 글의 시작부분에서 언급했던 HTTP 1.1 과 HTTPS RFCs에서 요청과 응답 헤더의 포맷에 관해 자세히 살펴 볼 수 있다. 300 레벨 응답에서, "Location:"는 리다이렉션을 위한 위치값을 제공해야만 한다. 301나 302 에러를 발생하는지를 확인하기 위해 다음 사항을 추가하자.
[code] private static boolean mustRedirect(int code){ if (code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_TEMP) { System.out.println("Received error " + code + ", trying secure redirect"); return true; } else return false; } [/code]리다이렉트가 자동적으로 처리되지 않는다면 사용자는 301 나 302 응답 코드만을 받게 된다는 것을 기억하자. 지금까지 이것은 리다이렉트가 HttpURLConnection가 아닌 HttpsURLConnection가 필요하다는 것을 의미했다. "Location:" 필드에 제공된 정보를 따르자. 다음과 같이 새로운 정보를 이용해서 url 값과 커넥션을 재설정하자.
[code] url = new URL("https://"+ url.getHost() + url.getFile()); connection = (HttpsURLConnection)url.openConnection(); [/code]마지막으로, 웹 리더를 받기 위해 위의 모든 사항을 모으고 필요할 때 보안 페이지로 리다이렉트될 수 있도록 하자.
[code] import javax.net.ssl.HttpsURLConnection; import java.net.URL; import java.net.MalformedURLException; import java.net.HttpURLConnection; import java.io.IOException; import java.io.BufferedReader; import java.io.InputStreamReader; public class RedirectingReader { private static HttpURLConnection connection; private static URL url; private static void connect(String urlString) { try { url = new URL(urlString); connection = (HttpURLConnection) url.openConnection(); //for jdk 1.5 or higher connection.setConnectionTimeout(1000); connection.setReadTimeout(3000); //for jdk 1.4 above System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "50000"); int code = connection.getResponseCode(); if (mustRedirect(code)) secureRedirect( connection.getHeaderField("Location")); readContents(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private static boolean mustRedirect(int code) { if (code == HttpURLConnection.HTTP_MOVED_PERM || code == HttpURLConnection.HTTP_MOVED_TEMP) { return true; } else return false; } private static void secureRedirect(String location) throws IOException { System.out.println(location); url = new URL(location); connection = (HttpsURLConnection) url.openConnection(); } private static void readContents() { BufferedReader in = null; try { in = new BufferedReader( new InputStreamReader( connection.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { System.out.println(inputLine); } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { if (args.length != 1) { System.err.println("usage: java WebPageReader " + "<url>"); System.exit(0); } connect(args[0]); } } [/code]업데이트된 RedirectingReader 를 컴파일하고 동작이 제대로 이루어지는지 확인하기 위해 몇 개의 다른 입력값을 넣어 실행해보자. 예를 들면 http://today.dev.java.net 와 http://linux.java.net를 넣어보자. 리다이렉트되지 않는 보안페이지와 비보안 페이지를 입력해서 RedirectingReader를 실행해도 동작이 적절하게 이루어져야 한다. 이제 다시 뒤로 돌아가서 최초 WebReader 프로그램에 모든 println() 메소드를 제거하고 readContents()를 추가할 수 있다.
@http://kr.sun.com/developers/techtips/c2004_02_10.html
우담아빠님 또 찾아주셨군요.. ^^
이번 주말엔 직장 동료 애기 돌 스냅사진을 부탁받았는데..
이런 부탁을 받으면 한편으론 기쁘면서도 또 한편으론
부담스러워 집니다.
카메라를 처음들었을때같은 편한 마음으로 찍는 스냅이라면 좋으련만
그런 행사장 사진은 저 보단 사진 받을 사람의 마음에 드는 결과물이
나와줘야 하기 때문에 아직은 제 사진에대한 확신이 없는 저로선
부담스러워 지는게 어찌보면 당연한 일이겠지요..