�빫�� ����� ���

Archive for the ‘번역’ Category.

만들어봐요 iPhone 게임 만들기 6 – Cocos2D for iPhone HelloWorld 분석

cocos2d for iPhone 샘플 코드를 작성하다가 위키를 또 그냥 번역하게 되었습니다.

이것 말고도 번역한 게 몇가지 더 있지만 일단 정리가 필요한 시점이니.. 코드에 몇가지 설명을 덧붙이겠습니다.

여기부터는 위키에 번역한 것을 고대로 갖다 붙여 놓겠습니다.

이 예제는 단순히 스크린에 라벨을 렌더링하는 기본 단계를 보여줄 뿐입니다. 초기화를 수행하는 다른 옵션들은 설명을 단순화하기 위해 이 예제에서는 설명하지 않을 것입니다. 이 예제를 이해하신다면 여러분은 hello_actions 예제를 보실 수 있을 겁니다.

구현 파일

FileHelloWorld.m

Import headers

c / c++에서와 같이 헤더를 임포트합니다.

// UIWindow, NSAutoReleasePool, 다른 객체를 사용하기 때문에
#import 
 
// 인터페이스 헤더
#import "HelloWorld.h"

레이어

HelloWorld 구현. 마법이 일어나는 곳입니다.

// HelloWorld implementation
@implementation HelloWorld
 
// 인스턴스 초기화 "init"
-(id) init
{
	// 항상 "super" init 호출하셔야 합니다.
	// 애플은 "super"의 리턴값을 "self"에 할당하는 것을 권장합니다.
	if( (self=[super init] )) {
 
		// 라벨 생성 및 초기화
		Label* label = [Label labelWithString:@"Hello World" fontName:@"Marker Felt" fontSize:64];
 
		// 디렉터에 윈도우 사이즈를 알아본다.
		CGSize size = [[Director sharedDirector] winSize];
 
		// 스크린의 한 가운데에 라벨을 위치시킨다.
		// 'ccp' is a helper macro that creates a point. It means: 'CoCos Point'
		label.position =  ccp( size.width /2 , size.height/2 );
 
		// 본 레이어의 자손으로 라벨 추가
		[self addChild: label];
	}
	return self;
}
 
// 할당된 객체들을 릴리즈할 때 "dealloc" 호출
- (void) dealloc
{
	// 이번 케이스에는 릴리즈할 필요가 전혀 없는 특별한 예제이므로 dealloc 할 것이 없습니다.
	// cocos2d는 자동으로 자손을 릴리즈합니다.(Label)
 
	// "super dealloc" 호출을 잊으시면 안됩니다.
	[super dealloc];
}
@end

어플리케이션 델리게이트

어플리케이션 델리게이트의 구현입니다. 아마 여러분의 모든 게임은 이 어플리케이션 델리게이트와 비슷할겁니다. 아래 코드를 이해하지 못하신다면 당장은 별로 중요하지 않을 겁니다.

@implementation AppController
 
// window 는 속성입니다. @synthesize 는 조상 메소드를 생성할 겁니다.
@synthesize window;
 
// 어플리케이션 엔트리 포인트
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
	// UIWindow 생성/초기화
	window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 
	// cocos2d를 window에 붙이고
	[[Director sharedDirector] attachInWindow:window];
 
	// 레이어를 생성하기 전에 수평 모드로 셋팅
	[[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
 
	// 윈도우를 visible로 만들기
	[window makeKeyAndVisible];
 
	// 아무것도 없는 Scene 초기화/생성
	Scene *scene = [Scene node];
 
	// HelloWorld 레이어 초기화/생성
	Layer *layer = [HelloWorld node];
	// HelloWorld 레이어를 메인 scene의 자손으로 붙임
	[scene addChild:layer];
 
	// 실행!
	[[Director sharedDirector] runWithScene: scene];
}
 
- (void) dealloc
{
	[window release];
	[super dealloc];
}
 
@end

메인 엔트리 포인트

objective-c도 c / c++ 처럼 메인 엔트리 포인트가 여전히 main함수입니다.

int main(int argc, char *argv[]) {
	// 보통 아래처럼 그냥 두는게 안전하지요. 이름만 "AppController"로 하는 것 빼고요.
	NSAutoreleasePool *pool = [NSAutoreleasePool new];
	UIApplicationMain(argc, argv, nil, @"AppController");
	[pool release];
	return 0;
}

헤더 파일

그리고 헤더파일:
FileHelloWorld.h

// 이 파일을 임포트 할 때 여러분은 모든 cocos2d 클래스를 임포트하게 됩니다.
#import "cocos2d.h"
 
// 어플리케이션 델리게이트 클래스
@interface AppController : NSObject <UIAccelerometerDelegate, UIAlertViewDelegate, UITextFieldDelegate, UIApplicationDelegate>
{
	// main UIWindow
	// OpenGL 뷰는 UIWindow의 뷰가 됩니다.
	UIWindow *window;
}
 
// 메인 UIWindow를 속성으로 만드세요.
@property (nonatomic, retain) UIWindow *window;
@end
 
// HelloWorld Layer
@interface HelloWorld : Layer
{
}
@end

일반적으로 iPhone App는 UIWindow(UIView)의 nib, Controller, Delegate를 만들어 MVC 패턴을 따르게 됩니다.(위에 처럼 간단한 어플리케이션이라면 Model은 빠지게 되겠죠.) 그래서 보통 Controller 코딩에 많은 시간을 할애하게 되죠. 그런데 cocos2d에서는 Delegate는 View에 CCDirector를 연결하는 것에 지나지 않게 됩니다. 특별히 여러 view를 사용하는지는 아직 모르겠습니다만, 보통 하나의 View에 n개의 Scene, m개의 Layer를 사용하는 것이 보통인 것 같습니다. 카드게임(고도리처럼)같이 간단한 흐름이라면 Scene도 하나만 사용할 수 있겠죠. 그래서 cocos2d 프로그래밍 가이드에 “대부분의 시간이 Layer를 만드는 것에 쓰여지게 됩니다.”라고 써 놓은 건지도 모르겠습니다.
Delegate 선언시 필요한 프로토콜(위에서는 UIAccelerometerDelegate, UIAlertViewDelegate, UITextFieldDelegate, UIApplicationDelegate)을 상속하여주면 cocos2d에서 UI 핸들링(터치와 같은)이 발생했을 때 구현된 메소드를 호출하게 됩니다. HelloWorld에는 UI핸들링이 없어서 예제코드가 없군요. HelloAction이나 HelloEvents에서 확인하실 수 있습니다.

이번엔 여기까지만 하고요, 다음에 이벤트와 액션을 살펴보도록 하지요.

How To Become A hacker (해커가 되는 방법) – Eric S. Raymond


The Art of Unix Programminig의 리뷰를 하려고 책 이미지를 찾던중 발견한 문서입니다.

원문은 http://www.tuxedo.org/~esr/faqs/hacker-howto.html 에서 볼 수 있으며
Korean translator: 6 June 2000, Song Chang-hun 송창훈 의 번역은
http://kr.geocities.com/kwon37xi/howtobecomeahacker.html 에서 볼 수 있습니다.

이 글은 송창훈님의 번역본을 옮긴 것입니다.

 

 

 

원제목 | How To Become A hacker

해커가 되는 방법

에릭 S. 레이먼드

Original copy: http://www.tuxedo.org/~esr/faqs/hacker-howto.html

Korean translator: 6 June 2000, Song Chang-hun 송창훈 <chsong@gnu.org>


Translation credits: 오역 및 오류 수정 사항은 errata 페이지를 참고하시기 바랍니다.

Printable file download: http://gnu.kldp.org/cb/hacker-howto/hacker-howto.hwp (30 KB)

If you want to check the original English text and Korean translation one-by-one, referring this:

http://gnu.kldp.org/cb/hacker-howto/hacker-howto.ko-checking.html

 

 


 

 

왜 이 문서를 만들었는가?

자곤 파일 (1)의 편집자로서, 나는 “어떻게 하면 일류 해커가 될 수 있는가?”라는 질문을 자주 받는다. 그러나 한가지 이상한 점은 이러한 중요한 질문에 대한 어떠한 FAQ나 문서도 없어 보인다는 것이었다. 이러한 이유로 인해서 나는 이 문서를 만들게 되었다.
이 문서의 최신 버전은 http://www.tuxedo.org/~esr/faqs/hacker-howto.html 사이트를 통해서 얻을 수 있다.

(1) 역자주:
자곤(jargon)이란 특정한 분야에서 사용되는 기술 전문 용어를 뜻하는 말이며, 이글에서 언급된 자곤 파일은 컴퓨터 분야의 용어들에 대한 정의를 담고 있는 일종의 용어 사전이다. 자곤 파일의 최신 버전은 4.0.0이며 1996년에 MIT 대학 출판부에 의해서『The New Hacker’s Dictionary』라는 제목의 단행본이 출판되기도 했다. 자곤 파일의 홈페이지는  http://www.tuxedo.org/jargon이며 GNU FTP 사이트를 통해서 다운받을 수 있다.

해커란 무엇인가?

자곤 파일에는 “해커”라는 단어에 대한 많은 정의들이 포함되어 있는데, 대부분 문제를 해결하고 한계를 극복하는 기술적인 숙련도와 명석함에 초점을 맞추고 있다. 그러나 만약 여러분이 해커가 되는 방법을 알고 싶다면 2가지만이 그 정의에 부합할 것이다.

숙련된 프로그래머들과 네트워크 전문가들이 문화를 공유하는 공동체가 있으며 그 역사는 최초의 시분할 미니 컴퓨터와 아르파넷(ARPAnet)이 실험되던 초기 시절인 수십년 전으로 거슬러 올라갈 수 있다 . 이 문화의 구성원들이 바로 “해커”라는 말의 기원이 되었다. 해커들은 인터넷을 만들었으며 현재와 같은 형태의 유닉스 운영체제를 만들었다. 또한 해커들은 유즈넷을 운영하고 WWW를 움직이게 만들고 있다. 만약 여러분이 이러한 문화에 속해 있거나 이러한 문화에 기여한 바가 있다면, 그리고 이 문화에 속해 있는 사람들이 여러분을 알고 있고 여러분을 해커라고 부르고 있다면 다름아닌 여러분이 자신이 바로 해커인 것이다.

해커의 사고 방식은 소프트웨어 해커 문화에 국한되지는 않는다. 예를 들면 해커의 마음가짐으로 전자 공학이나 음악과 같은
분야에 관심을 쏟는 사람도 있으며 실제로 고품격의 예술과 과학 분야에서 이러한 사실을 발견할 수 있다. 소프트웨어 해커들은 그들과 유사한 정신을 가진 문화가 있다는 것을 인정하며 그러한 문화에 참여하고 있는 사람들을 해커라고 부를 수도 있을 것이다. 그리고 어떤 해커들은 해커의 본질은 해커가 일하고 있는 특정한 분야나 수단에는 관계가 없다고 주장하기도 한다.
그러나 이글의 나머지 부분에서 우리는 소프트웨어 해커의 기술과 마음가짐, 그리고 해커라는 말의 기원이 된 공유된 문화의 전통에 대해서 조명해 볼 것이다.

실제로는 그렇지 않지만 자신들이 해커라고 큰소리로 주장하는 또하나의 집단이 있다. 이들은 주로 청소년기의 남자 아이들로 구성되며 컴퓨터 시스템에 침입하거나 전화를 무료로 사용할 수 있도록 시스템을 조작(2)하는 일을 한다. 그러나 실제 해커들은 이들을 “크래커(craker)”라고 부르며 자신들과 연관시키는 것을 원하지 않는다. 대부분의 해커들은 크래커들이 게으르고 무책임하며 그리 명석하지 않다고 생각한다. 또한 열쇠없이 자동차 시동을 걸 수 있다 (3)고 해서 자동차 엔지니어가 되는 것은 아니듯이 시스템의 보안을 뚫고 들어갈 수 있다는 것이 해커를 의미한다고 생각하지 않는다. 그러나 불행하게도 많은 저널리스트와 작자들은 크래커를 지칭할 때 해커라는 단어를 사용해 왔으며 이점이 해커들을 항상 못마땅하게 만들어 왔다.

(2) 역자주:
원어인 phone phreak는 전화를 무료로 사용할 수 있도록 시스템을 불법 개조하거나 조작하는 행위를 말한다.

(3) 역자주:
원어로 쓰인 hotwire는 점화 플러그에 연결된 전선을 단락시켜서 열쇠없이 자동차의 시동을 거는 것을 말한다.

해커와 크래커의 근본적인 차이점은 해커는 무엇인가를 만드는데 반해서 크래커는 그것을 파괴한다는 것이다.

만약 해커가 되고 싶다면 이글을 계속 읽어나가면 되겠지만 크래커가 되기를 원한다면 alt.2600 뉴스 그룹으로 가서 그곳의 글들을 읽고 5년에서 10년 정도의 세월을 교도소에서 보내게 될 크래킹을 준비하면 된다. 자신이 생각만큼 그리 똑똑하지 못하다는 사실을 깨달았을 때는 이미 늦은 후가될 것이다. 이것이 내가 크래커에 대해서 말하고 싶은 전부이다.

해커의 마음가짐

해커는 문제를 해결하고 무엇인가를 만들며 자유를 신뢰하고 자발적인 상호 협력을 믿는다. 해커로 인정받기 위해서는 이러한 마음가짐를 갖고 있는 것처럼 행동해야 할 뿐 아니라 진심으로 이러한 태도를 갖고 있어야만 한다.

그러나 단지 해커 문화 안에서 인정을 받기 위해서 이러한 태도를 취하고 있다면 그것은 중요한 점을 간과한 것이다. 그러한 것들을 믿는 사람이 된다는 것은 여러분 자신이 학습하는데 도움이 되고 지속적인 동기를 제공할 수 있기 때문에 중요한 일이다. 모든 창조적인 분야가 그러하듯이 대가가 되는 가장 효과적인 방법은 대가들의 품성과 태도를 지식뿐만 아니라 감성적인 측면까지 그대로 모방하는 것이다.

따라서 만약 해커가 되기를 원한다면, 다음과 같은 것들을 믿을 때까지 계속해서 반복하라.

 

1. 이 세계는 풀리지 않은 매력적인 문제들로 가득차 있다.

해커가 되는 것은 매우 재미있는 일이지만 많은 노력이 필요하며 그러한 노력을 기울이기 위해서는 동기가 부여되어야 한다. 성공한 운동 선수들은 자신의 신체를 발달시키거나 육체적 한계를 극복함으로써 얻을 수 있는 기쁨으로부터 동기를 부여받는다. 이와 유사하게 해커가 되기 위해서는 문제를 해결하고 자신의 기술을 숙련시키고 지적 능력을 훈련시키는 과정으로부터 원초적인 희열을 느낄 수 있어야 한다.

만약 여러분이 이러한 느낌을 가길 수 없는 사람이라면 해커가 되기 위해서 그러한 사람이 될 필요가 있다. 그렇지 않다면 여러분의 해킹 에너지는 섹스나 돈 그리고 명성과 같은 종류의 유혹에 의해서 약해질 것이다.

또한 문제를 해결하기 위해서 필요한 모든 사항을 알고 있지 못한다 하더라도 조그만 부분을 해결하면 그것을 통해서 나머지 부분들도 순차적으로 해결할 수 있다는 자신의 학습 능력에 대한 확신을 해커가 될 때까지 증진시켜 나가야 한다.

2. 이미 해결된 문제를 중복해서 풀어서는 안된다.

창조적인 사람은 매우 가치있고 제한된 자원이다. 따라서 이들은 새롭게 해결해야 할 매력적인 문제가 많이 있는 상황에서
이미 해결된 문제를 다시 해결하기 위해서 소모되어서는 안된다.

해커로서 행동하기 위해서는 다른 해커들의 연구 시간이 소중하다는 사실을 믿어야만 한다. 정보를 공유하는 것과 다른 해커들이 오래된 문제를 다시 해결하는 대신 새로운 문제를 해결할 수 있도록 자신이 해결한 문제의 해답을 다른 해커들에게 공개하는 것은 거의 도덕적인 의무 사항이다.

(자신의 창조적인 성과물을 모두 공개하는 해커들은 다른 해커들로부터 최상의 존경을 받겠지만, 이것이 자신의 성과물을 모두 공개해야만 한다는 의미는 아니다. 숙식을 해결하거나 컴퓨터를 사용하는 등의 생계를 유지하기 위해서 자신의 창조적인 성과물을 유료로 판매하는 것은 해커의 가치나 윤리에 전혀 어긋나지 않는다. 또한 가족을 부양하거나 심지어 많은 돈을 벌기 위해서 자신의 해킹 기술을 판매하는 행위도 그 일을 하고 있는 동안 자신이 해커라는 사실을 망각하지만 않는다면 문제되지 않는다.)

 

3. 권태와 단순 반복 작업은 해악이다.

일반적으로 창조적인 사람들과 해커들은 권태를 느끼거나 단순 반복적인 일을 계속해서는 안된다. 왜냐하면 이러한 것들은 새로운 문제를 해결할 수 없게 만들기 때문이다. 이렇게 인적 자원을 낭비되는 것은 모든 사람에게 유감스러운 일이다. 따라서 권태와 싫증은 단순한 나쁜 것이 아니라 실제적인 해악을 끼치는 것이다.

해커로서 행동하기 위해서는 자신을 위해서가 아니라 모든 사람들, 특히 다른 해커들을 위해서 자동화를 통해 가능한 모든 반복적인 요소를 없앨 수 있다고 믿어야만 한다.

(그러나 이러한 점에도 한가지 분명한 예외가 있다. 해커들은 때때로 의심점을 확인하거나 기술을 습득하기 위해서 또는 다른 방법을 통해서 얻을 수 없는 경험을 얻기 위해서 반복적이고 싫증을 느낄 만한 일을 하기도 한다. 하지만 이것은 선택에 의한 것일 뿐이다. 그 누구도 권태롭고 지루한 일을 강요 받아서는 안된다.)

 

4. 자유는 좋은 것이다.

해커들은 본질적으로 권위주의를 싫어한다. 여러분에게 명령을 내릴 수 있는 사람이 있다면 그 사람은 여러분이 매력을 느껴서 해결하고자 하는 문제를 풀지 못하도록 중단시킬 수도 있을 것이다. 또한 권위주의적인 방식으로 이루어진 일들은 일반적으로 끔찍할 정도로 어리석은 이유로 시작된 것들이다. 따라서 권위주의적인 태도를 발견하면 여러분과 다른 해커들이 질식하지 않기 위해서 싸워야만 한다.

(이것은 모든 권위에 대항해서 싸워야 한다는 것을 의미하는 것은 아니다. 아이들은 선도가 필요하며 범죄자들은 격리될 필요가 있다. 해커는 권위에 의한 명령을 따름으로써 동일한 시간에 자신이 원하는 것보다 많은 것들을 얻어낼 수 있다면 특정한 종류의 권위를 수용하는 것에 동의할 수 있다. 그러나 이러한 형태는 제한되고 의식적인 타협일 뿐이며 권위주의자들이 원하는 개인적인 복종의 형태가 되어서는 안된다.)

권위주의자들은 검열과 비밀을 통해서 세력을 확장한다. 그들은 자발적인 협력과 정보의 공유를 믿지 않고 오직 그들의 통제에 의한 협력만을 신뢰한다. 따라서 해커로서 행동하려면 검열과 비밀 그리고 자신의 의지에 반대되는 행동을 강요하기 위해서 사용되는 무력이나 기만에 대한 반감을 가져야 한다. 그리고 그러한 신념을 갖고 기꺼이 행동해야 한다.

5. 해커는 마음가짐만으로 되는 것이 아니다. 능력이 필요하다.

해커가 되기 위해서는 위에서 설명한 마음가짐들을 갖고 있어야만 한다. 그러나 이러한 태도를 갖고 있다고 해서 해커가 될 수 있는 것은 아니다. 하고자 하는 의욕이나 각오만으로 육상 챔피언이나 톱가수가 될 수 있는 것은 아니듯이 해커가 되기 위해서는 실질적인 노력이 필요하다. 해커가 되기 위해서는 지식과 노력 그리고 헌신과 고된 노동이 필요하다.

그러므로 올바른 자세만 갖고 있으면 된다는 생각을 버리고 모든 종류의 능력을 존중하라. 해커는 시간을 낭비해서는 안되고 어떠한 것에 대한 능력도 좋지만 특히 해킹 능력을 추구해야 한다. 소수의 사람만이 정통할 수 있는 기술에 대한 능력은 좋은 것이다. 더욱이 정신적인 예리함과 기술 그리고 집중이 필요한 기술에 대한 능력은 최상의 것이다.

만약 여러분이 능력을 외경한다면 여러분 자신이 그러한 능력을 가질 수 있도록 노력하는 것을 즐기게 될 것이다. 힘든 작업과 헌신은 권태나 싫증이라기 보다 강도 높고 즐거운 놀이가 될 것이다. 이것은 해커가 되기 위해서 필수적인 부분이다.

 

 

기본적인 해킹 기술

해커에게는 마음가짐이 중요하지만 기술은 더욱 중요하다. 마음가짐과 태도가 능력을 대신할 수 있는 것은 아니다. 해커라고 불리기 위해서는 다음과 같은 기본적인 기술들을 갖고 있어야 한다.

그러나 이러한 종류의 기술들은 새로운 기술이 개발되고 기존의 기술이 낡고 쓸모없는 것이 되어감에 따라서 천천히 바뀌어 간다. 예를 들면, 기계어를 이용한 프로그래밍이 예전에 필요한 기본적인 해킹 기술이었고 HTML은 최근에야 포함된 것이다. 그러나 현 시점에서는 다음과 같은 것들이 필요하다.

1. 프로그래밍을 학습하라.

프로그램을 만드는 방법을 배우는 것은 해킹의 가장 기본적인 기술이다. 어떠한 컴퓨터 언어도 모르고 있는 상태라면
Python(파이썬) 언어부터 학습할 것을 권하고 싶다. 파이썬은 매우 간결하게 디자인되어 있으며, 관련 문서가 풍부하고 비교적 초보자에게 쉬운 언어이다. 초보자들에게 좋은 언어임에도 불구하고 파이썬은 결코 단순한 작업만을 위한 언어는 아니다. 파이썬 언어는 매우 강력하고 유연하며 대형 프로젝트에 매우 적합한 언어이다.

그러나 한가지 종류의 언어만을 알고 있다면 해커나 단순한 프로그래머 수준의 기술에도 결코 도달하지 못한다는 사실을 알고 있어야 한다. 이러한 수준에 도달하기 위해서는 특정한 언어가 아닌 일반적인 관점으로 프로그래밍 문제에 접근하는 방법을 배울 필요가 있다. 진정한 해커가 되기 위해서는 기존에 알고 있던 지식과 매뉴얼에 설명된 새로운 언어의 내용을 연관시켜서 빠른 시일안에 새로운 언어를 학습할 수 있는 능력을 키워야 한다. 이것은 몇가지 다른 언어를 배워야만 한다는 사실을 의미한다.

만약 본격적인 프로그래밍을 하고 싶다면 유닉스의 핵심 언어인 C 언어를 배워야만 한다.(그럼에도 불구하고 C 언어는
초보자들이 처음에 배울만한 언어는 아니다.) C 언어 이외에 해커들에게 특히 중요한 언어는 Perl(펄)LISP(리스프)이다. Perl은 실용적인 이유로 인해서 배울 가치가 있다. Perl은 동적 웹페이지를 만들기 위한 CGI와 시스템 관리를 위한 스크립트를
작성하기 위해서 매우 널리 사용된다. 따라서 Perl 언어로 프로그램을 만들 일이 없다고 하더라고 Perl 소스를 해독할 수 있는 능력을 갖고 있어야 한다. LISP은 심오한 개발의 경험을 얻고 싶을 경우에 배울만한 훌륭한 언어이다. 이러한 경험은 여러분들이 LISP을 실제로 사용하지 않게 되더라도 남은 날들을 통해서 여러분들이 보다 좋은 프로그래머가 될 수 있도록 만들어 줄 것이다.

Python과 C, Perl, LISP 네개의 언어를 모두 배우는 것이 실제로 최상의 방법이다. 이들은 가장 중요한 해킹 언어라는 점 이외에도 프로그래밍에 대한 다른 접근 방법을 갖고 있기 때문에 여러분들에게 프로그래밍에 대한 가치있는 방법들을 알려줄 것이다.

프로그램을 작성하는 방법은 매우 복잡한 기술이기 때문에 이글에서 구체적이고 완벽한 지식을 설명할 수는 없다. 그러나 책과 교육 과정이 이러한 기술을 알려줄 수 있는 것은 아니라는 사실은 말할 수 있다. 많은, 아마도 대부분의 일류 해커들은 독학을 하고 있으며 (a) 코드를 읽는 것과 (b) 코드를 직접 작성해 보는 것으로 프로그램을 만드는 방법을 배울 수 있다.

프로그래밍을 배우는 것은 컴퓨터 언어가 아닌 일반적인 언어 작문을 배우는 것과 같다. 좋은 프로그램을 만드는 방법은 대가가 만든 코드를 읽고 자신이 직접 프로그램을 만들어 보는 것이며, 또다른 소스를 읽고 다시 만들어 보고 더욱 많은 소스를 읽고 또다시 만들어 보는 과정을 자신이 구상했던 형태에 맞는 힘과 효율성을 갖출 때까지 계속해서 되풀이 하는 것이다.

참고할 만한 좋은 코드를 발견하는 것은 일반적으로 어려운 일이다. 왜냐하면 초보 해커들이 읽고 변형해 볼 수 있을 만한
대형 프로그램의 소스 코드를 구하는 일이 매우 힘들기 때문이다. 그러나 이러한 상황은 이제 극적으로 바뀌었다. 왜냐하면 오픈 소스 소프트웨어와 프로그래밍 툴 그리고 해커들이 만든 운영체제의 소스 코드를 이제 쉽게 구할 수 있기 때문이다.

 

2. 오픈 소스 유닉스 중의 하나를 구해서 학습하고 운영해 보자.

우선, 여러분이 개인용 컴퓨터를 갖고 있거나 이러한 시스템을 사용할 수 있다는 전제하에 얘기를 해 보고자 한다.(실제로 오늘날에는 이러한 것들을 어렵지 않게 가질 수 있다.:-)) 초심자들이 해킹 기술을 습득할 수 있는 가장 확실한 첫번째 단계는
리눅스나 BSD 유닉스 중의 하나를 구해서 개인용 컴퓨터에 설치한 뒤에 이를 운영해 보는 것이다.

이 세상에는 유닉스 이외에도 많은 운영체제들이 있다. 그러나 이러한 운영체제들은 바이너리 형태로만 배포되기 때문에
소스 코드를 읽거나 수정할 수 없다. DOS나 윈도우즈 또는 MAcOS가 설치된 시스템에서 해킹 기술을 배우려고 하는 것은
마치 깁스를 하고 춤을 배우려고 하는 것과 같다.

유닉스는 인터넷 운영체제이다. 유닉스를 모르는 상태에서도 인터넷을 이용하는 방법을 배울 수는 있지만, 유닉스를 모르면 결코 인터넷 해커가 될 수 없다. 이러한 이유로 인해서 오늘날의 해커 문화는 특히 유닉스를 중심으로 이루어져 있다.(해커 문화의 중심이 언제나 유닉스 중심으로 이루어 졌던 것은 아니며 몇몇 오래된 해커들은 이러한 부분에 대해서 여전히 불만스러워 한다. 그러나 유닉스와 인터넷의 공생 관계는 마이크로소프트의 세력이 그것을 약화시키지 못할 정도로 꾸준히 강화되어 왔다.)

따라서 유닉스를 배우고 익혀서 가능한 많은 시간을 함께 보내고 소스 코드를 읽고 코드를 수정해 가면서 시스템을 운영해
볼 것을 권하고 싶다.(개인적으로 나는 리눅스를 선호하지만 동일한 컴퓨터에 리눅스와 DOS/윈도우즈를 함께 설치하는 방법을 사용할 수도 있다.) 여러분은 마이크로소프트의 운영체제에서보다 월등하게 좋은 C와 LISP, Python, Perl을 포함한 프로그래밍 툴들을 얻을 수 있다. 여러분은 즐거움을 얻을 수 있을 것이며 또한 훗날 대가가 된 후에 뒤돌아 보기 전까지는 알 수 없을 만큼 자신이 배울 수 있다고 생각하는 것 이상의 많은 지식을 얻을 수 있을 것이다.

유닉스를 학습하는데 필요한 보다 많은 사항들에 대해서는 Loginataka (4)를 참고하기 바란다.

(4) 역자주:
Loginataka는 1993년에 “Dialogue between a Guru and a Newbie”라는 부제를 달고 유닉스 그루(guru)가 되기를 원하는 사람의 질문에 대한 답변으로 유즈넷에 올려진 글이다. Loginataka는 범어(Sanskrit)로 축도(祝禱, blessing)라는 뜻을 담고 있다. 뉴스그룹에 올려진 최초의 글은 이 곳을 통해서 참고할 수 있다.

리눅스를 구하기 위해서 http://linuxresources.com/apps/ftp.html 사이트를 참고할 수 있으며, BSD 유닉스에 대한 정보와 도움말은 http://www.bsd.org/ 사이트를 통해서 구할 수 있다.

(주의: 만약 여러분이 초보자라면 리눅스나 BSD를 혼자서 설치할 것을 권하지 않는다. 리눅스의 경우에는 리눅스 유저 그룹의 도움을 요청하거나 리눅스 인터넷 지원 협동 조합(Linux Internet Support Co-operative)를 통해서 방법을 찾을 수 있을 것이다. LISC는 여러분들이 도움을 얻을 수 있는 IRC 채널을 운영하고 있다.

 

3. WWW를 사용하는 방법과 HTML을 작성하는 방법을 배우라.

해커 문화가 만들어 온 대부분의 것들은 눈에 보이지 않게 움직이며 공장과 사무실 그리고 대학들이 일반인들의 삶에서 충돌없이 운영되도록 도와준다. 하나의 큰 예외인 웹은 해커들의 빛나는 커다란 작품으로 정치가들 조차 그것이 이 세상을 바꾸도 있다고 인정하고 있다. 다른 많은 이유들이 있지만, 이 한가지 사실만으로도 웹이 어떻게 움직이는 가를 배울 필요성이 있다.

웹의 작동 방법을 배운다는 것은 누구나 할 수 있는 브라우저 사용법을 배운다는 것이 아니라 웹의 마크업(markup) 언어인 HTML을 작성하는 방법을 배운다는 것을 의미한다. 만일 여러분이 프로그램을 작성하는 방법을 모르고 있다면 HTML을 작성하는 방법은 프로그래밍을 위한 사고 방식에 약간의 도움이 될 것이다. 따라서 HTML을 이용해서 홈페이지를 만들어 보자.

그러나 단지 홈페이지를 만드는 것이 해커가 되는 길에 근접하는 것은 아니다. 웹은 많은 홈페이지로 가득 차 있으며 별다른
가치를 갖고 있지 않거나 내용도 없는 경우가 많다. (다음의 페이지를 통해서 바람직하지 않은 HTML 사용 예를 참고할 수
있다.

http://www.tuxedo.org/~esr/html-hell.html

홈페이지를 가치 있게 만들기 위해서는 의미있는 내용이 있어야 하며 다른 해커들에게 흥미나 도움을 줄 수 있어야 한다. 그럼, 이제 다음 주제로 넘어가 보자.

해커 문화 안에서의 지위

화폐 경제가 개입되지 않는 대부분의 문화가 그러하듯이 해커 문화는 명성으로 움직인다. (5) 여러분이 흥미있는 문제를 풀려고 노력하고 있다면, 그것이 얼마나 흥미있는 것인지 그리고 여러분의 해답이 얼마나 좋은 것인지는 일반적으로 기술적으로 같은 수준의 동료나 기술적 우위가 있는 사람에 의해서 판단될 수 있다.

(5) 역자주:

인지권의 개간
에서 이러한 측면을 자세하게 분석하고 있다.

따라서 해커 게임을 한다고 할 때, 다른 해커들이 여러분의 기술을 어떻게 평가하느냐에 의해서 여러분의 점수가 정해진다는 점을 알고 있어야 한다.(이러한 이유로 인해서 다른 해커들이 여러분을 계속해서 해커라고 부르기 전까지는 여러분은 해커가 아닌 것이다.) 그러나 이러한 사실은 해킹은 혼자서 작업하는 것이라는 이미지와 현재는 점차 사라져 가는 경향이 있기는 하지만 아직도 유효한 자아와 다른 사람들의 인정이 본인의 동기에 영향을 준다는 것에 반대하는 해커 문화의 금기에 의해서 가려져 있다.

특히, 해커 문화는 인류학자들이 말하는 “증여 문화” (6)에 해당된다. 해커 문화에서는 다른 사람을 지배하거나 아름다워지거나 또는 다른 사람들이 원하는 것을 획득하는 방법을 통해서가 아니라 자신의 것을 주는 방법으로 지위와 명성을 얻을 수 있다. 특히, 자신의 시간과 창의성, 기술 결과물들을 무상으로 공개하는 방법으로 지위와 명성을 얻을 수 있다.

(6) 역자주:

인지권의 개간에서 이러한 측면을 자세하게 분석하고 있다.

다음은 다른 해커들의 존경을 받을 수 있는 다섯 가지 기본적인 것들이다.

1. 오픈 소스 소프트웨어를 작성하라.

가장 전통적이고 기본적인 방법은 다른 해커들이 흥미를 느끼거나 유용하게 사용할 수 있는 프로그램을 만들고 프로그램의 소스 코드를 해커 문화에서 쓸 수 있도록 공개하는 것이다.

(우리는 이러한 프로그램을 “자유 소프트웨어(free software)”라고 부른다. 그러나 자유 소프트웨어에서 사용된 “자유”의 영어 표기인 ”free”는 자유와 무료라는 두가지 뜻을 모두 갖고 있기 때문에 사람들에게 그 정확한 의미가 무엇인지 혼동하게 만들곤 한다. 이러한 이유로 인해서 근래에는 “오픈 소스” 소프트웨어라는 말을 보다 많이 사용한다.

해커 문화 안에서 존경받는 대부분의 해커들은 많은 사람들의 요구를 충족시킬 수 있는 광범위하고 유용한 프로그램을 많은 사람들이 사용할 수 있도록 하기 위해서 무료로 공개한 사람들이다.

2. 오픈 소스 소프트웨어의 테스트와 디버깅에 참여하라.

오픈 소스 소프트웨어의 디버깅에 참여하는 사람도 해커들의 인정을 받을 수 있다. 불완전한 현실 세계에서는 필연적으로 소프트웨어 개발 시간의 대부분을 디버깅에 할애할 수밖에 없다. 바로 이러한 점이 오픈 소스 개발자들이 좋은 베타 테스터를 그들의 몸무게 만큼의 보석과 같은 가치가 있다고 생각하는 이유이다. 좋은 베타 테스터는 상태을 명확하게 설명할 수 있고, 문제의 원인을 잘 찾아낼 수 있으며 촉박하게 만들어진 프로그램에 포함된 버그들을 참을성 있게 테스트 할 수 있고 또한 기꺼이 간단한 진단 테스트를 수행해 줄 수 있는 사람이다. 오랫동안 지연되는 악몽 같은 디버깅 작업이 되느냐 아니면 단지 가벼운 골치거리가 되는가의 차이는 심지어 이러한 기준 중 한가지에 의해서 달라지기도 한다.

만약 여러분이 유닉스의 초심자라면 흥미를 갖고 좋은 베타 테스터가 될 수 있을만한 프로그램을 찾아서 참여해 보라. 이것은 프로그램을 테스트하는 것을 돕는 것으로부터 시작해서 프로그램을 디버깅하는 단계로, 그리고 프로그램을 개작하는 단계까지 자연스럽게 발전할 수 있는 방법이다. 이러한 방법을 통해서 많은 것을 배울 수 있으며 훗날 여러분을 도와줄 사람들과 좋은 인연을 만들 수 있을 것이다.

3. 인터넷으로 유용한 정보를 제공하라.

해커들의 존경을 받을 수 있는 또하나의 좋은 방법은 흥미있고 유용한 정보들을 모아서 웹페이지나 FAQ 형식의 문서로 만든 뒤에 다른 사람들이 쉽게 이용할 수 있도록 제공하는 것이다.

주요 기술 FAQ의 매인테이너들은 오픈 소스 프로그램 저작자에 버금가는 존경을 받는다.

4. 하부 구조를 유지하는 작업에 참여하라.

해커 문화 — 실제로 인터넷의 기술 개발은 자원자들에 의해서 운영되는 것이다. (7) 인터넷이 지속되기 위해서는 메일링 리스트를 관리하거나 뉴스 그룹을 조정하는 일 그리고 대규모 소프트웨어 아카이브 사이트를 유지하고 RFC와 기타 기술적인
표준 문서들을 개발하는 것과 같은 멋지고 재미있지는 않지만 꼭 필요한 일들이 많이 필요하다.

(7) 역자주:
인터넷과 해커 문화의 하부 구조를 유지하는 작업은 인터넷 엔지니어링 태스크 포스와 밀접한 관계를 갖고 있다. IETF에 대한
자세한 사항은 http://gnu.kldp.org/opensources/ietf.ko.html 문서를 통해서 참고할 수 있다.

이러한 종류의 일을 하는 사람들은 많은 존경을 받는다. 왜냐하면 이러한 일에는 많은 시간이 투자되어야 하며 프로그램 코드를 갖고 작업하는 일들보다 지루한 작업이라는 것을 모든 사람들이 알고 있기 때문이다. 이러한 일은 한다는 것은 해커 문화와 공동체에 헌신한다는 것을 의미한다.

5. 해커 문화 자체에 봉사하라.

마지막 방법은 해커 문화 자체에 기여하고 해커 문화를 보다 널리 확산시키는 것이다. 예를 들면, 이글과 같은 해커가 되는 방법에 대한 확실한 입문서를 쓰는 일 등이다. :-) 이와 같은 일은 많은 경험이 필요하거나 앞에서 언급한 4가지 사항들을 잘 모르는 상태에서도 할 수 있는 것이다.

정확히 말해서 해커 문화는 지도자를 갖고 있지 않다. 그러나 영웅과 장로 그리고 역사가와 선구자들은 존재한다. 만약 여러분이 해커 문화 안에서 오랫 동안 남아있다면 그러한 사람들 중 한 사람이 될 수도 있을 것이다. 그러나 해커들은 자신을 떠들썩하게 내세우는 장로를 싫어하기 때문에 그러한 평판을 얻는 것은 위험하다는 사실을 명심하라. 명성과 지위를 획득하려고 분투하지 말고 그것이 자신에게 스스로 찾아올 수 있도록 노력하라. 그리고 자신이 얻은 지위에 대해서 겸손하고 감사해야 한다.

해커와 널드와의 관계

널리 알려진 신화에도 불구하고 해커가 되기 위해서 널드(Nerd) (8)가 될 필요는 없다. 그러나 널드가 되는 것이 해커가 되는데 도움이 되며 실제로 많은 해커들은 널드이기도 하다. 사회적인 접촉으로부터 자신을 격리시키는 것은 사색과 해킹과 같은 중요한 일에 집중하는데 도움이 된다.

(8) 역자주:

널드(nerd)란 MIT의 해커 문화에서 유래된 말로 사회적인 접촉을 멀리하고 오직 하나의 일, 특히 컴퓨터와 기술 분야의 일을 위해서 자신의 모든 역량을 집중하는 사람을 말한다. 이와는 달리 BBS에 접속하거나 인터넷을 활용하는 등의 컴퓨터와 관련된 사회적 활동을 활발히 하는 사람들은 지크(geek)라고 말한다. 널드와 지크는 사전적인 의미로 모두 “바보”, “괴짜” 또는
“멍청이”라는 뜻을 갖고 있지만 해커 문화에서 사용하는 의미는 이와는 조금 다른 것이다. 해커와 널드, 지크는 모두 조금씩 다른 뜻을 갖고 있지만 이글에서는 거의 같은 의미로 사용되고 있다.

이러한 이유로 인해서 많은 해커들은 자신이 널드라고 불려지거나 심지어 조금 더 농도짙은 표현인 지크(geek)라고 불려지는
것을 자랑스럽게 생각한다. 이러한 형태은 자신이 사회의 일상적인 예상과 기대로부터 벗어나 있는 자유로운 존재, 즉 아웃사이더라고 선언하는 방법의 하나인 것이다. 보다 자세한 사항에 대해서는 지크 페이지  http://samsara.circus.com/~omni/geek.html을 참고하기 바란다.

만약 여러분이 해킹에 전념해서 좋은 성과를 얻을 수 있는 동시에 생활 또한 만족스럽게 유지할 수 있다면 이것은 매우 좋은
것이다. 이것은 내가 초심자였던 1970년대보다 근래에 보다 수월해 졌다. 왜냐하면 현재의 메인스트림 문화는 널드에 대해서 보다 우호적이기 때문이다. 심지어 해커가 보다 좋은 배우자감이라고 인정하는 사람들도 점점 늘어나고 있다. 보다 자세한 것은 여성들을 위한 지크 가이드 사이트 http://www.bunnyhop.com/BH5/geekguys.html을 참조하기 바란다.

만약 여러분이 일상적이고 정상적인 생활을 갖고 있지 않았기 때문에 해킹에 매력을 느끼게 되었다고 해도 그또한 나쁘지 않다. 최소한 집중하는데는 문제가 없을 것이기 때문이다. 시간이 가면 아마도 정상적인 생활로 돌아가서 집중하는데 방해가 되는 문제를 갖게 될 것이다.

해커에게 어울리는 것들

다시 한번 반복하면 해커가 되기 위해서는 해커들의 정신을 익히지 않으면 안된다. 여기에 여러분이 컴퓨터 앞에 없을 때 할 수 있을 만한 것들이 있다. 이러한 것들이 해킹을 대신할 수 있는 것은 아니지만 많은 해커들이 이러한 것들을 하고 있으며 해킹의 본질과 통하는 기본적인 공통점이 있다고 느끼고 있다.

 

  • 공상 과학 소설을 읽으라. 공상 과학 소설 모임에 참석하라.(이것은 해커와 원로 해커들을 만날 수 있는 좋은 방법이다.)
  • 선(禪)과 태권도와 같은 격투기를 배우라.(이러한 것들의 정신적인 훈련은 해킹과 유사한 면이 있다.)
  • 음악을 분석적으로 듣을 수 있도록 노력하라. 특정한 쟝르의 음악을 평가할 수 있도록 견문을 넓히라. 악기를 잘 연주하거나 노래를 잘 부를 수 있게 연습하라.
  • 농담이나 언어 유희에 대한 이해를 높이라.
  • 자신의 모국어를 잘 사용할 수 있도록 훈련하라.(내가 아는 최고의 해커들을 포함해서 매우 많은 해커들이 작자로서의 소양을 갖고 있다.)

이러한 것들 중에서 이미 하고 있는 것이 많으면 많을 수록 해커로서의 자질 또한 많이 갖고 있다고 할 수 있다. 왜 이러한 것들이 도움이 되는 지에 대해서 명백하게 밝혀진 것은 없지만, 이러한 것들은 우뇌와 좌뇌를 균형있게 사용하게 하는데 도움이 되는 것으로 보인다.(해커에게는 논리적인 추론 능력과 문제를 직관적으로 파악할 수 있는 감성이 모두 필요하다.)

마지막으로, 해서는 안 될 다음과 같은 것들에 주의하기 바란다.

  • 어리석고 과장된 이름이나 영화에 나온 이름을 ID로 사용하지 말라.
  • 유즈넷(또는 어느 장소에서도)에서 싸움에 끼어들지 말라.
  • 자신을 사이버펑크(cyberpunk)라고 부르지 말라.
  • 자신의 시간을 누구나 하고 있는 일에 소모하지 말라.
  • 철자가 틀리고 문법에 맞지 않는 메일을 보내거나 포스팅하지 말라.

위와 같은 것들을 해서 얻을 수 있는 명성은 비난과 조소뿐이다. 해커들은 쉽게 잊지 않는다. 따라서 자신의 실수를 만회하기 위해서는 많은 세월이 필요하다.

영화에서 나온 이름이나 별명을 ID로 사용하는데 따른 문제점은 별명을 이용해서 자신의 신분을 감추는 것이 warez d00dz (9)이나 다른 저급한 형태의 크래커들이 갖고 있는 어리석고 치기어린 행동 양식이기 때문이다. 해커들이 이렇게 하지 않는다. 해커들을 그들이 해 놓은 것들을 자랑스럽게 여기며 여기에 그들의 실명이 관계되는 것을 원한다. 따라서 만약 여러분이 별명을 갖고 있다면 그것을 버리도록 하라. 해커 문화에서 별명을 사용하는 것은 자신이 3류라는 것을 의미할 뿐이다.

(9) 역자주:
warez d00dz는 BBS를 중심으로 활동하는 크래커들이다. warez doodz는 (soft)wares와 dude를 발음대로 표기한 것이며 정품소프트웨어가 판매되는 당일에 이를 크래킹한 버전을 동시에 해적 BBS를 통해서 유포하는 것으로 유명하다. 최근의 크랙 활동으로는 2000년 6월 24일에 블리자드사의 디아블로2의 영문 복제판을 해적 BBS에 올린 바 있다.

참고할 만한 다른 문서들

피터 씨바흐(Peter Seebach)는 해커들을 어떻게 처리해야 할 지 이해하지 못하고 난감해 하는 관리자들을 위한 훌륭한 해커 FAQ 문서를 유지하고 있다. 피터의 사이트가 접속상에 문제가 있다면 다음의 검색 결과를 통해서 해당 문서를 얻을 수
있을 것이다.  Excite 검색

유닉스 해커의 자세와 바람직한 교육 방법에 대해서는 전술했던 Loginataka를 참고할 수 있다.

또다른 나의 글인 해커 문화의 짧은 역사도 도움이 될 것이다.

리눅스와 오픈 소스 문화가 어떻게 움직이는 가에 대해서 자세히 설명한 논문인 성당과 시장과 그 주제를 보다 세부적으로 분석한 속편인 인지권의 개간도 참고가 될 수 있을 것이다.

빈번한 질문들에 대한 답변

Q: 해킹 방법을 가르쳐 줄 수 있는가?

이글이 발표된 직후부터 일주일에 몇번씩(가끔은 하루에도 몇번씩) “해킹 방법에 대한 모든 것을 가르켜달라.”는 메일을 받고 있다. 그러나 불행히도 나는 그럴만한 충분한 시간과 에너지가 없으며 나 자신의 해킹 프로젝트에만도 내 시간의 110% 이상을 투자해도 모자란 것이 현실이다.

비록 내가 해킹 방법을 알려줄 수 있다고 해도 해킹은 기본적으로 자기 스스로가 익혀나가야하는 기술과 태도이다. 만약 여러분을 도와줄 해커를 찾았다고 해도 여러분이 지식을 수동적으로 받기만을 원한다면 그들로 부터 존중받지 못하게 될 것이다.

몇가지 정도를 먼저 배우고 노력해 보라. 학습할 수 있는 능력이 있다는 것을 스스로 입증해 보라. 그런 후에 갖게 되는 특정한 질문들을 해커들에게 물어보도록 하라.

Q: 어떻게 시작해야 하는가? 그런 후에는 무엇을 해야 하는가?

최선의 방법은 LUG (Linux User Group)를 이용하는 것이다. LUG에 대한 정보는 LDP (Linux Documentation Project) 사이트의 리눅스 일반 정보 페이지를 통해서참고할 수 있다. (10) 아마도 자신의 대학이나 학교와 관련된 가까운 곳의 LUG 정보를 얻을 수 있을 것이다. LUG 멤버들은 여러분이 요청한다면 리눅스 배포판을 제공해 주거나 설치와 사용에 관한 도움을 줄 것이다.

(10) 역자주:
한국의 경우에는 KLDP 사이트에 있는 http://kldp.org/script/links/페이지에 포함된 LUG 세부 항목이나 리눅스 공동체 세미나 사이트 http://seminar.klug.or.kr를 을 통해서 LUG 정보를 참고할 수 있다.

Q: 시작하기에 너무 늦은 나이인 것 같다. 당신은 언제부터 시작했는가?

시작하고 싶은 동기를 느낀 나이가 가장 좋은 나이이다. 늦었다고 생각할 때가 가장 빠른 때인 것이다. 대부분의 사람들은 15에서 20살 사이에 시작하는 것 같다. 하지만 나는 이보다 많은 나이나 적은 나이에 시작한 경우를 모두 알고 있다.

Q: 해킹을 배우는데는 시간이 얼마나 걸리는가?

갖고 있는 재능과 노력을 얼마나 열심히 하느냐에 달려있다. 대부분의 경우에는 최선의 노력을 한다고 할 때 약 18개월에서 2년 정도의 시간이면 고급 기술들을 익힐 수 있다. 그러나 거기에 만족해서는 안된다. 만약 진정한 해커가 되고자 한다면 자신의 기술을 갈고 닦기 위해서 남은 여생을 모두 투자해야 한다.

Q: 처음 배우는 언어로 비주얼 베이식과 델파이가 적당한가?

그렇지 않다. 왜냐하면 이러한 언어들은 이식성이 없기 때문이다. 이 언어들은 오픈 소스 차원에서 구현되지 못하기 때문에 제작 업체가 지원하기로 결정한 플랫폼에 한정될 수밖에 없다. 이러한 독점적인 상태를 인정하는 것은 해커의 방식이 아니다.

비주얼 베이식은 더욱 그러하다. 마이크로소프트의 독점적인 언어라는 사실 만으로도 충분한 이유가 되며, 다른 종류의 베이식 언어와 마찬가지로 비주얼 베이식은 잘못된 프로그래밍 습관에 젖게 하는 잘못 디자인된 언어이다.

Q: 시스템을 크랙하는 것을 도와주거나 그 방법을 가르쳐 줄 수 있는가?

No.이 FAQ를 읽고도 여전히 이러한 종류의 메일을 보낼 수 있는 사람은 비록 내게 그러한 시간이 있다고 하더라고 교육을 받을 수 없을 만큼 어리석은 사람이다. 나는 이러한 종류의 질문을 담고 있는 메일들을 모두 무시하거나 극도로 거친 답장을 보낸다.

Q: 시스템이 크랙을 당했다. 앞으로의 공격을 막을 수 있게 도와줄 수 있는가?

도와줄 수 없다. 이런 종류의 질문을 하는 사람들은 십중팔구 윈도우즈 시스템을 사용하는 사람들이다. 윈도우즈는 코드와 아키텍처에 너무도 많은 결함이 존해하기 때문에 시스템 크랙에 효과적으로 대처하는 것이 불가능하다. 그것은 마치 보트로 새들어 오는 물을 체로 퍼내는 것과 같다. 가장 신뢰할 만한 대책은 리눅스나 보안을 담보할 수 있는 다른 운영체제로 교체하는 것이다.

Q: 얘기를 나눌 수 있는 실제 해커들은 어디서 만날 수 있는가?

가장 좋은 방법은 모임에 참석할 수 있는 주변의 리눅스 유저 그룹에 참여하는 것이다. 참고할 수 있는 유저 그룹들의 목록은 LDP사이트를 통해서 얻을 수 있다.

(나는 IRC에서는 실제로 해커를 만날 수 없다고 말하곤 했다. 그러나 이제는 상황이 변화되었다고 생각된다. GIMP나 Perl 등과 같이 특정한 해커 공동체들은 이제 IRC 채널을 갖고 있다.)

Q: 해킹에 관련된 유용한 책에는 어떤 것들이 있는가?

내가 유지하고 있는 리눅스 도서 목록 HOWTO 문서가 도움이 될 수 있을 것이다. 이 문서의 한글 번역문은 다음의 사이트를 통해서 참고할 수 있다.

http://kldp.org/HOWTO/html/Reading-List/Reading-List-HOWTO.html.

또다른 문서인 Loginataka 또한 흥미를 줄 수 있을 것이다.

Q: 어떤 언어를 제일 먼저 배워야 하는가?

아직 HTML을 모르는 상태라면 제일 먼저 배워야 할 언어는 HTML이다. 겉치레만 요란하고 내실없는 HTML 책들이 많이
있지만 내가 생각하기에 제일 좋은 책 중의 하나라고 판단되는 것은 HTML 핵심 가이드이다.

그러나 HTML은 본격적인 프로그래밍 언어는 아니다. 프로그래밍을 시작할 준비가 되었을 때는 Python 부터 학습할 것을
권하고 싶다. 많은 사람들이 추천하고 있는 Perl은 아직까지는 Python보다 널리 사용되고 있지만, 배우기 어렵고 또한 개인적으로 잘 디자인된 언어는 아니라고 생각한다. Python을 배울 때 참고할 수 있는 자료들은 다음의 웹사이트를 통해서 참고할 수 있다. http://www.deja.com/getdoc.xp?AN=523189453.

C는 정말로 중요한 언어이다. 그러나 이것은 Python이나 Perl 보다도 어려운 언어이기 때문에 처음부터 C를 학습하려고 하지 말라.

MS 윈도우즈 사용자들은 비주얼 베이식에 안주하지 말아야 한다. VB는 프로그래밍에 대한 나쁜 습관을 가르쳐 주고 윈도우즈 이외의 다른 아키텍처로 이식될 수 없기 때문에 피하는 것이 좋다.

Q: 마이트로소프트를 증오하고 경멸해야 하는가?

그렇게 하지 말라. 마이크로소프트가 혐오스럽지 않기 때문에 그렇게 하지 말라는 것이 아니라 해커 문화는 마이크로소프트가 존재하기 오래전부터 존재하고 있었으며, 마이크로소프트가 역사 속으로 사라진 후에도 계속해서 남아있을 것이기 때문이다. 마이크로소프트를 비난하고 증오하는데 사용할 에너지가 있다면 오히려 자신의 기술을 보다 가다듬는데 사용하라. 좋은 코드를 작성하는 것 — 그것은 자신의 이미지를 손상시키지 않고도 마이크로소프트를 충분히 물리칠 수 있는 길이다.

Q: 오픈 소스 소프트웨어는 프로그래머들이 생계를 유지하는데 방해가 되지 않는가?

그런 것 같지는 않다. 현재까지 오픈 소스 산업은 취업 기회를 감소시킨 것이 아니라 오히려 일자리를 만들고 있다. 이미 만들어져 있는 프로그램을 활용하는 것이 그렇지 않은 경우보다 실제적인 경제적 이익을 가져올 수 있다면 새로운 프로그램을 완성한 후에 그것을 무료로 공개하느냐의 여부에 관계없이 프로그래머는 보수를 받을 수 있을 것이다. 얼마나 많은 “무료” 소프트웨어가 만들어지건 간에 보다 많은 수의 새롭고 커스터마이즈된 애플리케이션 프로그램이 필요하기 마련이다. 오픈 소스 페이지를 참고하면 이러한 측면에 대한 보다 구체적인 글을 참고할 수 있다.

Q: 해커가 되려면 어떻게 시작해야 하며, 공개 유닉스는 어디서 구할 수 있는가?

이글을 어딘가를 찾아보면 가장 널리 사용되는 공개 유닉스를 구할 수 있는 방법을 알 수 있다. 해커가 되기 위해서는 무엇보다도 동기와 진취적인 정신 그리고 자기 스스로를 교육시킬 수 있는 능력이 필요하다. 그럼, 이제 시작해 보자!

Project Darkstar Server Application Tutorial (번역)

Project Darkstar Server Application Tutorial

목차

(Java bindings for OpenGL : https://jogl.dev.java.net/)
  

  • 소개 (Introduction)
  • Project Darksart Server Application 코딩하기 (Coding Project Darkstar Server Applications)
    • 목표와 철학 (Goals and Philosophy)
    • 실행하기 (Approach to Execution)
      • 작업과 관리 (Tasks and Managers)
      • 작업 순번 (Task Ordering)
      • 작업 실행시간 (Task Lifetime)
    • Managed Object와 Managed Reference (Managed Objects and Managed References)
      • Managed Reference를 통하여 Managed Object 접근하기 (Accessing Managed Objects through Managed References)
      • 나만의 Managed Object 디자인하기 (Designing Your Managed Objects)
      • The Player Managed Object (The Player Managed Object)
      • The AppListener (The AppListener)
    • 서버 API 클래스 위치하기 (Locating the Server API Classes)
    • System Classes and Interfaces (System Classes and Interfaces)
    • Task Manager Classes and Interfaces (Task Manager Classes and Interfaces)
    • Data Manager Classes and Interfaces (Data Manager Classes and Interfaces)
    • Channel Manager Classes and Interfaces (Channel Manager Classes and Interfaces)
  • Lesson One: Hello World!
    • HelloWorld 코딩하기 (Coding HelloWorld)
      • HelloWorld
    • HelloWorld 실행하기 (Running HelloWorld)
      • HelloWorld 실행하기 (Running HelloWorld)
  • Lesson Two: Hello Logger!
    • HelloLogger 코딩하기 (Coding HelloLogger)
      • HelloLogger
    • The Logging Properties File
  • Lesson 3: Tasks, Managers, and Hello Timer
    • Tasks (Tasks)
    • Managers (Managers)
    • HelloTimer 코딩하기 (Coding HelloTimer)
      • HelloTimer
  • Lesson 4: Hello Persistence!
    • HelloPersistence 코딩하기 (Coding HelloPersistence)
      • HelloPersistence
    • HelloPersistence2 코딩하기 (Coding HelloPersistence2)
      • HelloPersistence2
      • TrivalTimedTask
  •  
    • HelloPersistence3 코딩하기 (Coding HelloPersistence3)
    •  
      • HelloPersistence3 26
  • Lesson 5: Hello User!
    • 사용자가 로그인할 때 일어나는 일 (Knowing When a User Logs In)
    •  
      • HelloUser
    • 직접 통신 (Direct Communication)
    •  
      • HelloUser2
      • HelloUserSessionListener
      • HelloEchoSessionListener
    • 예제 실행하기 (Running the Examples)
  • Lesson 6: Hello Channels!
    • HelloChannels 코딩하기 (Coding HelloChannels)
      • HelloChannels
      • HelloChannelsSessionListener
      • HelloChannelsChannelListener
    • HelloChannels 실행하기 (Running HelloChannels)
  • 결론 (Conclusion)
    • Best Practices
    • 튜토리얼에 없는 내용 (Things Not Covered in This Tutorial)
  • Appendix A: SwordWorld Example Code
    • Sword World
    • SwordWorldObject
    • SwordWorldRoom
    • SwordWorldPlayer



소개 (Introduction)

Project Darkstar Server(PDS)의 튜토리얼로의 여행을 환영합니다. 이 문서는 PDS상의 게입서버를 만들기 위하여 필요한 모든 것을 알려주기 위해 작성되었습니다. 우리는(역자주:아마 PDS를 진행하는 개발자들이겠죠?) 이러한 프로그램을 PDS 어플리케이션이라고 부르고, 여러분은 이 용어를 이 튜토리얼과 다른 PDS 문서들에서 보게 될 것입니다. 이 튜토리얼은 PDS 어플리케이션을 어떻게 만들 수 있는지에 대한 개략으로 시작해서 한 단계씩 간단한 어플리케이션을 개발하여보겠습니다.
Welcome to the Project Darkstar Server (PDS) application tutorial. This document is designed to teach you everything you need to know to start writing game servers that run on top of the Project Darkstar Server. We call such programs PDS applications, and you will see that term used in this and other PDS documents. This tutorial begins with an overview of how to code a PDS application, then steps through the development of a very simple application.



PDS 어플리케이션 코딩하기 (Coding Project Darkstar Server Applications)

이 챕터는 PDS 환경에서 어덯게 게임 서버 어플리케이션을 코딩하는지에 대한 기초 컨셉을 알려줍니다. 해당 컨셉들을 이해한다는 것은 규모가 크고 신뢰성이 있으며 오류에 강하고 견고한 네트웍 게임을 만드는 첫번째 단계입니다.
This chapter presents the fundamental concepts of how to code game server applications in the Project Darkstar Server (PDS) environment. Understanding these concepts is the first step on the path to building massively scalable, reliable, fault-tolerant, and persistent network games.



목표와 철학 (Goals and Philosophy)
PDS 코딩 모델을 이해하기 위해 PDS 시스템의 설계 목표를 이해하는 것이 좋습니다. 기본적인 목표는 다음과 같습니다.
In order to understand the Project Darkstar Server (PDS) coding model, it is useful to understand the system’s goals. The fundamental goals are as follows:
  

  • 신뢰성이 있으며 견고하고 대규모에 적용가능하며 오류에 강한 게임 개발자에게 투명한 서버측 코딩을 할 수 있어야 한다.
  • 개발자에게 단일 스레드 이벤트 기반 프로그래밍 모델을 제공하여야 한다. 개발자의 다른 이벤트등에 대한 오류가 전파되지 않도록 하여야 한다.
  • Make server-side game code reliable, scalable, persistent, and fault-tolerant in a manner that is transparent to the game developer.
  • Present a simple single-threaded event-driven programming model to the developer. The developer should never have his or her code fail due to interactions between code handling different events. Applications coded to run in the PDS environment are called PDS applications.



실행 접근성 (Approach to Execution)

태스크와 매니저 (Tasks and Managers)
PDS 어플리케이션 프로그래머 측면에서 보면 PDS 어플리케이션은 확실히 단일 스레드와 이벤트 기반 모델로 실행됩니다. 어떤 데이터가 수정되거나 하는 하나의 이벤트를 다루는 코더에 의해서 이벤트 핸들링 코딩됩니다. 그러므로 실행이란 결과에 대한 증명과 데드락에 대한 증명으로 생각 될 수 있습니다. 사실, 동기화가 전혀 필요 없는 대부분의 상태라는 조건하에 Managed Object에서 synchronized 키워드를 시도하는 것은 해결하기 어려운 버그를 초래할 수 있습니다.
실제로 이 시스템(PDS)는 각각의 이벤트를 처리하기 위해 상당수의 스레드를 사용합니다. 이 스레드들을 컨트롤 하는 것을 태스크(tasks)라고 합니다. PDS는 각각의 무슨 태스크가 억세스하는 데이터들을 추적합니다. 충돌을 피하기 위하여, 이전에 생성된 태스크가 제대로 종료되도록 최근 생성된 태스크는 중지되며 스케쥴링됩니다.
태스크는 PDS 매니저(PDS Manager)에 의해 생성됩니다. PDS 환경 안팍으로 PDS 어플리케이션은 메니저를 사용하여 효과적인 액션을 취할 수 있습니다. PDS에는 세가지 기본 매니저가 있습니다. 또한 PDS 환경에 임의의 매니저를 추가할 수 있습니다. 세가지 기본 매니저는 다음과 같습니다.
From the point of view of the PDS application programmer, PDS applications execute in an apparently monothreaded, event-driven model. The code handling the event appears to the coder to have sole ownership of any data it modifies. Thus, execution is both race-proof and deadlock-proof. Under most conditions there is no need to synchronize application code and, in fact, attempting to use the synchronized keyword in Managed Objects1 can cause subtle bugs.
In actuality, the system has many threads of control all simultaneously processing their own events. These threads of control are called tasks. The system keeps track of what data each task accesses. Should a conflict arise, the younger task is aborted and scheduled for retry at a later date so that the older task can complete and get out of the way. (“Younger” and “older” refer to the time at which the task was queued for execution.)
Tasks are created by PDS managers. a PDS application uses these managers to effect actions in the PDS environment and the outside world. There are three standard managers in the system. In addition, arbitrary managers may be coded and added to the PDS environment. The standard managers are:
  

  • Task Manager
태스크 매니저를 사용하여 PDS 어플리케이션에서 큐를 사용할 수 있습니다. 한 태스크는 자식 태스크를 생성할 수 있습니다. 즉시 또는 지연 후 실생되거나 주기적으로 실생되기 위해 큐에 추가될 수 있습니다. 형제 태스크라고 말할 수 있는 다중 자식 태스크는 같은 한 부보 태스크에 의해 큐에 추가됩니다.
A PDS application can use the Task Manager to queue tasks of its own. Tasks can be queued for immediate execution, delayed execution, or periodic execution. Tasks created from within other tasks are called child tasks. The task that queued the child task is called the parent task. Multiple child tasks queued by the same parent task are called sibling tasks.
  • Data Manager
PDS 어플리캐이션은 데이터 매니저를 사용하여 Managed Object라고 하는 퍼시스턴스 클래스 또는 분산 자바 오브젝트를 생성할 수 있습니다.
A PDS application can use the Data Manager to create and access persistent, distributed Java objects called Managed Objects. The PDS application is itself composed of Managed Objects
  • Channel Manager
채널 매니저는 데이터 채널을 컨트롤 하기 위하여 사용됩니다. 이 데이터 채널은 보통 서로 다른 클라이언트나 서버-클라이언트의 통신을 위해 사용됩니다.
A PDS application can use the Channel Manager to create and control publish/subscribe data channels. These data channels are used to communicate between different clients and between clients and the server.

PDS 어플리케이션은 AppContext class를 사용하여 다른 매니저를 접근하는 PDS 핵심 기능에 접근합니다.
A PDS application gets access to core PDS functionality through the AppContext class, which provides methods to obtain references to the various managers.



태스크 정리하기 (Task Ordering)
클라이언트로부터 생성된 이벤트를 핸들링하는 태스크는 특정 규칙에 의해 실행이 보증됩니다. 한 클라이언트의 이전 이벤트가 모두 끝나기 전에는 그 이후 발생될 클라이언트 이벤트를 핸들링하는 태스크가 실행하지 않을 것입니다. 자식 태스크는 그 부모 태스크를 주목하고 있으나 형제 태스크에 관하여는 그렇지 않습니다. 이것은 자식 태스크의 실행이 부모 태스크의 실행이 끝나기 전 까지 시작되지 않는 것을 의미합니다. 그러나 형제 태스크에 관한 순서에 대한 보증은 존재하지 않습니다.
중요 : PDS에서 이외의 다른 태스크 순서에 대한 보증은 더이상 있지 않습니다. 실제적으로, 서로 다른 유저의 이벤트를 핸들링에 대한 실행은 한 서버 안에서도 각각의 다른 태스크의 실행 시작 순서를 보증하지 않습니다.
Tasks that handle events created by a client are guaranteed to execute in order. A task to handle a client event that occurred later in time will not start executing until the tasks to handle all earlier events generated by the same client have finished. A child task is ordered with regard to its parent task, but not with regard to its siblings. This means that execution of a child task will not begin until execution of the parent has completed. There are, however, no guarantees among sibling tasks as to order of execution.
Important: There are no other order guarantees in the system. In particular, execution of tasks to handle events of different users are not guaranteed to start executing relative to each other in the order they arrived at the server.

태스크 생명주기 Task Lifetime
다른 태스크에 요청될 수 있는 자원들의 과도한 요청으로 블럭되지 않기 위해 태스크는 짧은 주기로 제공됩니다. PDS 자체는 최대한의 실행시간으로 설정되어 있습니다(디폴트 1분). 각각의 설정대로 아직 종료되지 않은 다른 태스크는 시스템에 의해 강제로 종료될 것입니다. 만약 어떤 태스크의 실행 기간이 너무 길다면 실행시간을 줄일 수 있는 두가지 방법이 제공됩니다.
Tasks are intended to be short-lived so that they do not block access for an inordinate amount of time to resources that might be needed by other tasks. The PDS is configured by its operator with a maximum task execution time (the default is one minute). Any task that does not finish within that time will be forcibly terminated by the system.
If you have a task that runs too long, there are two approaches to reducing its execution time:
  

  • 자식 태스크의 연결로 각각 나누거나, 문제되는 한 부분마다 핸들러를 추가하거나 다음 태스크로 설정하여 순차적으로 큐에 넣는 것입니다. 보통 continuation-passing style 이라고 알려진 방법입니다.(연결-통과 스타일? -_-)
Split it up into a chain of child tasks, each of which handles one discrete portion of the problem, and then queues the next task in sequence. This is known as continuation-passing style.
  • 소비되는 시간의 계산이 종료되었을 때의 결과를 갖는 태스크를 큐에 넣어 처리하는 방법입니다.
  • Move the time-consuming calculations into a custom manager that queues a result-task when the calculations are complete.

이 접근법들은 각각 장단점이 있습니다. 첫번째 방법은 태스크들을 잘게 쪼갤 수 있을 때 문제를 해결하는데 더 쉽습니다. 주의할 것은, 각 단계가 실행될 때 어떠한 정확한 보증도 없기 때문에, 데이터를 포함한 각각의 태스크가 민감하고 불안한 상태로 있게 된다는 것입니다. 이 접근 방법의 특별한 경우는 여러 문제의 부분들이 각각 다른 위치에 개별적이고 핸들링 가능한 경우입니다. 이러한 경우, 종료까지 필요한 시간은 각각 병렬로 실행되는 태스크의 연결상태에 의해 줄어들 수도 있습니다. 그러나 이 별개의 연결상태는 각각 서로 정확하지 않은 순서로 있을 수 있으므로 그 상태의 태스크들의 수행은 반드시 서로 독립적이어야 합니다. 두번째 접근법은 태스크를 작은 상태로 분해할 수 없을 때 적용하기 쉽습니다. 그러나 이 방법은 여러분이 커스텀 PDS 매니저를 만들고 설치하여야 합니다. (커스텀 PDS 매니저를 만드는 것은 ‘PDS 환경을 확장하는 방법’에 관한 문서를 보시기 바랍니다.)
특별히 중요한 경우는 태스크의 실행주기가 좀 더 긴 것을 블럭하기 위해 시스템 콜을 호출하는 경우입니다. 이 경우 반드시 정교한 커스텀 매니저를 개발하는 것이 필요합니다.
Each approach has its advantages and disadvantages. The first is easier for simple problems that lend themselves to serial decomposition. Care must be taken that each task ends with the data in a sensible and usable state, because there is no guarantee as to exactly when the next step will be executed. A special case of this approach is where parts of the problem are separable and handleable in parallel. In this case, the time to complete may be reduced by launching parallel chains of tasks. These parallel chains, however,
have no guaranteed ordering in relation to each other, so the work they perform must really be independent of each other, The second approach is easier for problems that don’t decompose well into small, discrete components; however, it requires the writing and installation of a custom PDS manager. (Writing custom PDS managers will be covered by a separate document explaining how to extend the PDS environment.)
A particularly important case is code that has to go into system calls that can block for more then the task execution lifetime. These must be implemented through a custom manager in order to produce a robust PDS application.



Managed Objects and Managed References (역자주 : 원어 그대로 사용하는 용어이고 다른 용어가 생각나질 않아서 그대로 둡니다.)
데이터 매니저는 오브젝트 스토어라고 하는 오프젝트의 풀에 저장된 매니지드 오브젝트의 퍼시스턴스 셋을 보존하고 있습니다. 보통 자바 오브젝트와 같이, 각 매니지드 오브젝트는 데이터와 데이터를 처리하기 위한 메소드를 포함합니다. 매니지드 오브젝트를 작성하려면 ManagedObject와 Serializable 인터페이스를 상속하여야 합니다. 매니지드 오브젝트는 오브젝트 풀에 해당 오브젝트가 있는 동안 오브젝트 소토어의 풀의 한 부분이 되지 않습니다. 이는 매니지드 레퍼런스를 상속받은 오브젝트 또는 오브젝트를 상속받은 오브젝트는 데이터 매니저에 의해 결정된다는 것입니다.
매니지드 레퍼런스는 전형적인 J2SE(tm)의 레퍼런스 오브젝트와 같습니다(예를 들면, SoftReference, WeakReference). 매니지드 오브젝트는 반드시 다른 매니지드 레퍼런스를 통하여 다른 매니지드 오브젝트를 참조하여야 합니다. 이는 매니지드 오브젝트의 한 상태의 부분인 매니지드 오브젝트의 컴포넌트 오브젝트를 레퍼런스 하는 것과, 자신의 상태를 포함한 서로 다른 매니지드 오브젝트를 레퍼런스 하는 것에 대한 차이점을 데이터 매니저가 어떻게 표할 수 있는지에 대한 것입니다.
매니지드 오브젝트가 데이터 매니저에서 getBinding 을 호출하는 또다른 태스크에 의해 오브젝트 스토어로부터 갱신될 수도 있을 만큼, 이 바인딩한 이름은 매니지드 오브젝트와 함께 스트링에 첨가시킵니다.
The Data Manager maintains a persistent set of Managed Objects stored in a pool of objects called the Object Store. Like a normal Java object, each Managed Object contains both data and the methods to act upon that data. In order to be a Managed Object, the object must implement both the ManagedObject and Serializable interfaces. A Managed Object does not become part of the Object Store’s pool until the pool is made aware of the object. This is done by using the Data Manager either to request a Managed Reference to the object or to bind a name to the object.
A Managed Reference is a reference object that looks much like the J2SE(tm) reference objects (for example, SoftReference, WeakReference). Managed Objects must refer to other Managed Objects through Managed References. This is how the Data Manager can tell the difference between a reference to a component object of the Managed Object (for instance, a list) that is part of that Managed Object’s state, and a reference to a separate Managed Object with a state of its own.
A name binding associates a string with the Managed Object such that the object may be retrieved from the Object Store by other tasks using the getBinding call on the Data Manager.

매니지드 레퍼런스를 통하여서 매니지드 오브젝트 억세스하기 (Accessing Managed Objects through Managed References)
매니지드 레퍼런스는 get과 getForUpdate 두 개의 메소드를 억세스할 수 있습니다. 두 메소드 모두 오브젝트의 복사본을 리턴합니다. 두 메소드의 차이점은 다음과 같습니다.
The Managed Reference has two access methods: get and getForUpdate. Both methods return a task-local copy of the object. The difference between the two methods is:
  

  • getForUpdate는 매니지드 오브젝트의 상태가 변경될 것이라고 시스템에 통보합니다.
getForUpdate informs the system that you intend to modify the state of the Managed Object.
  • get은 단지 상태를 read할 때 사용됩니다.
get says you intend only to read the state but not write it.

다른 매니지드 오브젝트의 모든 변경점이 변경되지 않는다 하여도(get을 통한 억세스를 포함하여), 여러분이 특정 시간에 매니지드 오브젝트의 상태가 변경될 것을 예상하고 있다면 getForUpdate를 사용하는 것이 좀 더 효율적입니다. 이는 시스템이 태스크와 훨씬 유효하고 빨리 생성된 핸들러와 태스크 사이의 충돌을 탐지하는 것을 가능하게 합니다.
반대로 말하면, 매니지드 오브젝트의 상태가 변경되지 않을 것이라면 get을 사용하는 것이 더 낫습니다. get을 호출하는 것은 다중 태스크가 동작하는 상태에서 매니지드 오브젝트에 대하여 좀 더 병렬적으로 접근하는 것을 가능하게 합니다. 여러분이 오브젝트의 상태를 수정하는 것을 알고 있는 시점이라면, 데이터 매니저의 markForUpdate를 호출하여 get에서 getForUpdate까지 접근을 올릴 수 있습니다. (업데이트를 하기 위해 똑같은 매니지드 오브젝트를 확인하는 다중 호출은 위험하지 않습니다.)
같은 태스트 내의 동등한 매니지드 레퍼런스상의 get 또는 getForUpdate 호출로의 Subsequent 호출은 같은 태스크의 복사본을 리턴할 것입니다.
getBinding을 호출하여 바인딩된 이름의 오브젝트를 갱신할 수 있습니다. 이는 매니지드 오브젝트의 get을 호출하는 것과 같아서, 해당 오브젝트를 변경할 예정이라면 여러분은 그 오브젝트를 갱신한 후 markForUpdate를 호출하여야 할 것입니다.
오브젝트 스토어 내의 매니지드 오브젝트들은 가비지 컬렉션에 영향을 받지 않습니다. 일단 매내지드 오브젝트가 스토어에 있다는 것만 알려주면, 데이터 매니저의 removeObject를 호출하여 오브젝트 스토어에서 명시적으로 제거되기 전까지 해당 오브젝트의 상태가 유지됩니다. 이는 어플리케이션이 더이상 필요 없을 때 오브젝트 스토어에서 오브젝트를 제거하고 생명주기를 관리하여야 한다는 것을 말합니다. 이를 잘못한다면 성능과 메모리 낭비라는 결과를 초래할 수 있습니다. 마찬가지로, 네임 바인딩은 removeBinding을 호출하여 제거하기 전까지 메모리상에 있을 것입니다. 네임 바인딩은 제거된 오브젝트를 참조하고 있을 때에도 사라지지 않습니다.
Although all changes to any Managed Object are persistent (even those accessed via get), it is more efficient to use getForUpdate if you know at that time that you are going to want to modify the Managed Object’s state. This allows the system to detect conflicts between tasks and handle them earlier and with greater efficiency.
Conversely, it is better to use get if the state of the Managed Object may not be modified. The get call can allow for more parallel access to the Managed Object from multiple tasks. If you reach a point later in the execution where you know you are going to modify the object’s state, you can upgrade your access from get to getForUpdate by calling the markForUpdate method on the Data Manager. (Multiple calls to mark the same Managed Object for update are harmless.)
Subsequent calls to get or getForUpdate on equivalent Managed References in the same task will return the same task-local copy.
You can also retrieve an object with a bound name by calling getBinding. This is equivalent to a get call on a Managed Reference to the object, so, if you intend to modify the object’s state, you should call markForUpdate after retrieving the object.
Managed Objects in the Object Store are not garbage-collected. Once the store is made aware of a Managed Object, it keeps the state of that object until it is explicitly removed from the object store with a call to the removeObject call on the Data Manager. It is up to the application to manage the life cycle of Managed Objects and to remove them from the Object Store when they are no longer needed. Failure to do so may result in garbage building up in your Object Store and impacting its performance. Likewise, name bindings are stored until explicitly destroyed with removeBinding. A name binding is not removed when the object it refers to is removed.




자신만의 매니지드 오브젝트 설계하기 (Designing Your Managed Objects)
일반적으로 매니지드 오브젝트는 세가지 타입입니다.
Managed Objects typically fall into three general types of entity:
  

  • 칼, 몬스터, 활동공간(예를들면 방) 등과 같은 게임상의 가상환경에 동작하는 오브젝트.
Actual objects in your game’s simulated environment, such as a sword, a monster, or a play-space (such as a room).
  • 근접 계산을 위한 quau-tree나 이동 경로 결정 work-mesh와 같은 로직 또는 데이터 구조.
Purely logical or data constructs such as a quad-tree for determining player-proximity or a walk-mesh to determine movement paths.
  • 게임 플레이어를 위한 매니지드 오브젝트 프록시
Proxies for human players in the world of Managed Objects.
Figure 1) 한 방안의 두명의 게임 플레이어와 칼 하나를 포함한 방의 간단한 월드 구조 개요
Figure 1 below illustrates a very basic world consisting of a single room that contains two players and a sword.
  

Figure 1: Example of a simple ManagedObject world


다중 매니지드 오브젝트상의 데이터 전송을 결정할 때 다음 세가지를 고려하여야 합니다.
When deciding where to break data up into multiple Managed Objects, consider these questions:
  

  • 얼마나 큰 데이터입니까? 자신의 상태에 한 개의 매니지드 오브젝트를 포함하는 큰 데이터는 읽고 쓰는데 더 많은 시간을 요합니다.
How big is the data? The more data a single Managed Object encompasses in its state, the more time it takes to load and save.
  • 그 데이터의 결합도가 얼마나 가깝습니까? 일반적으로 공용으로 사용되는 데이터는 똑같은 매니지드 오브젝트에서 좀 더 효과적으로 저장됩니다. 독립적으로 사용되는 데이터는 다른 매니지드 오브젝트상으로 구분되기 위한 대상입니다. 핵심적으로 수정되어야 하는 데이터는 최우선적으로 같은 매니지드 오브젝트에 저장되어야 합니다.
How closely coupled is the data? Data that are generally accessed together are more efficiently stored in the same Managed Object. Data that are accessed independently are candidates for separation onto different Managed Objects. Data that have to be modified atomically are best stored in the same Managed Object.
  • 특정 데이터를 동시에 접근하여야 하는 태스크들이 얼마나 많습니까? 위에 언급한 것에 따르면, PDS는 가능한한 병렬로 실행되는 많은 태스크와 같이 실행되기 위한 최선책을 수행합니다. 다중 병령 수행되는 태스크가 같은 매니지드 오브젝트의 상태를 변경할 때 충돌을 피하는 것은 많은 비용이 소요됩니다.
How many simultaneous tasks are going to need access to this data? As explained above, the PDS does its best to execute as many tasks in parallel as it can. Resolving the conflicts that arise when multiple parallel tasks want to change the state of the same ManagedObject can be expensive.
get과 같이 공유될 수 있는 데이터를 업데이트하기 위하여 데이터에 lock을 거는 것이 데이터를 나누기 위한 최선책입니다. 단지 읽히는 데이터가 다중 태스크에 읽혀지는 것에의해 공유되는 것에 반하여, 업데이트되어야할 데이터는 업데이트하는 태스크에 의하여 수행되어야 합니다. 다중 태스크가 적어도 하나 이상의 태스크에 의해 업데이트되는 매니지드 오브젝트상의 항목들에 접근하여야 할 때, 매니지드 오브젝트는 병복에 걸릴 수 있습니다. 최고의 성능을 위하여 가능한한 적은 병목을 원할 것입니다.
It is best to split up data that has to be locked for update from data that can be shared with a get. Data that is going to be updated has to be owned by the updating task, whereas data that is just read can be shared by multiple reading tasks. When multiple tasks have to access fields on a Managed Object that is being updated by at least one of them, that Managed Object becomes a potential bottleneck. For best performance, you want as few bottleneck Managed Objects as possible.

이러한 고찰을 통하여 볼 때 세번째 고려사항은 잘 동작하는 PDS 어플리케이션을 작성하기 위하여 가장 크리티컬한 주제입니다.

Of all these considerations, the third is the most critical to a well-running PDS application.


플레이어 매니지드 오브젝트 (The Player Managed Object)
외부에서 이벤트가 발생할 때 매니저에 의한 이벤트 핸들러가 get 메소드를 호출하기 위하여 매니지드 오브젝트는 그 자신을 등록할 것입니다. 첫번째 가장 중요한 매니지드 오브젝트 타입이 플레이어 매니지드 오브젝트 입니다. 플레이어 매니지드 오브젝트는 ClientSessionListener 인터페이스를 상속하고 AppListener의 loggedIn 콜백으로부터 리턴값을 받을 때 시스템에 리턴됩니다. 이 후, 어떤 입력되는 데이터 패킷과 게임 플레이어의 접속해제 이벤트에 의해 호출될 것입니다.
플레이어 매니지드 오브젝트는 여러 매니지드 오브젝트 사이에서 마치 플레이어와 같이 동작을 수행할 것입니다. 게임 플레이어는 PDS Client API를 사용하여 서버로 데이터 패킷을 보냅니다. 이는 플레이어 매니지드 오브젝트의 userDataReceived 메소드를 호출하는 태스크의 결과로 시스템에서 userDataReceived 이벤트를 발생시킵니다. 플레이어 매니지드 오브젝트는 이를 수행함을 찾아내기 위해 패킷을 분석한 후, 필요한 자기 자신의 역할을 실행하며 요청된 태스크를 완료하기 위하여 다른 매니지드 오브젝트를 실행합니다.
Managed Objects register themselves as event handlers with a manager in order to get called when outside events occur. One very important type of Managed Object is the Player Managed Object. A Player Managed Object implements the ClientSessionListener interface and is returned to the system as the return value from the loggedIn callback on the AppListener. From then on, it will get called for any incoming data packets and disconnect events from that player.
The Player Managed Object acts as a proxy for the player in the world of Managed Objects. The player sends data packets to the server using the PDS Client API. This causes a userDataReceived event in the system, which results in a task that calls the Player Managed Object’s userDataReceived method. The Player Managed Object should parse the packet to find out what it is supposed to do, and then act on itself and other Managed Objects in order to accomplish the requested task.


Figure 2) 두 게임 플레이어가 PDS에 클라이언트로 연결되는 간단한 매니지드 오브젝트 구조
Figure 2 shows our simple Managed Object world, with two players connected to the PDS as clients.


Figure 2: Client connections to the simple ManagedObject world


플레이어 매니지드 오브젝트는 룸 매니지드 오브젝트를 포인팅하는 매니지드 레퍼런스인 “current room” 필드를 갖고 있습니다. 룸 매니지드 오브젝트는 매니지드 레퍼런스의 리스트인 인벤토리 리스트를 갖고 있습니다. 현재, 이 리스트에는 세 아이템들이 있습니다. 두 게임 플레이어와 한 개의 칼입니다. 이들 각각은 매니지드 오브젝트를 표현하고 있습니다(Player 1 Managed Object, Player 2 Managed Object, and Sword Managed Object).
The Player Managed Objects have a “current room” field, which is a Managed Reference that points to the Room Managed Object. The Room Managed Object has an inventory list, which is a list of Managed References. Currently, there are three items in the list: the two players and a sword. Each is represented by a Managed Object (Player 1 Managed Object, Player 2 Managed Object, and Sword Managed Object).


The AppListener
룸 매니지드 오브젝트의 매니지드 오브젝트를 구성하는 월드 상위에, 소드 매니지드 오브젝트와 한 상의 플레이어 매니지드 오브젝트가 있습니다. 그러나 PDS에서 처음 게임을 시작할 때 매니지드 오브젝트의 월드는 이와같지 않습니다. 사실 그것은 다음과 같습니다.
Above we had a world of Managed Objects consisting of a Room Managed Object, a Sword Managed Object and a couple of Player Managed Objects. However, when we start the game in the PDS for the first time, the world of Managed Objects doesn’t look like that. In fact it looks like this:


말하고자 하는 것은, 그렇습니다. 텅 비어 있다는 겁니다.
첫번째로 오브젝트 스토어에 있는 매니지드 오브잭트가 얼마나 되겠습니까?
그 때답은 AppListener라는 특별한 매니지드 오브젝트 하나입니다. AppListener를 특별하게 하는 두가지가 있습니다.
  

  • AppListener 인터페이스를 상속하면 다음 두 메소드를 정의하여야 합니다.
    • initialize
    • loggedIn
  • 해당 어플리케이션의 AppListener에 상세히 기술되었습니다.

Which is to say, it is empty.
How then do the Managed Objects get into the Object Store in the first place?
The answer is a special Managed Object called the AppListener. There are two special things about the class that defines the AppListener:

  • It implements the AppListener interface. This interface defines two methods:
    • initialize
    • loggedIn
  • It has been specified as the AppListener class for this application.

다음 방법을 따라 두가지 속성을 결합합니다.
These two properties combine in the following way:

  • PDS가 부팅될 때(또는 PDS로 새로운 어플리케이션이 인스톨될 때), PDS는 오브젝트 스토어에 해당 어플리케이션에 동작하는 AppListener를 위치시킬 것입니다.
Upon the boot of the PDS (or the installation of a new application into the PDS), the PDS attempts to locate the AppListener for that application in the Object Store.
  • 어플리케이션이 이전에 부팅된 적이 없다면, 이 어플리케이션의 오브젝트 스토어는 공백상태가 될 것이며 PDS는 AppListener의 동작에 실패할 것입니다. 이러한 이유로 어플리케이션은 매니지드 오브젝트인 AppListener를 생성해야 하며 초기화를 호출하는 태스크를 시작하여야 합니다.
If the application has never been booted before, then its Object Store in the PDS is blank (as in Figure 3), and the PDS will fail to find the AppListener. In that case, it creates the AppListener Managed Object itself, and then starts a task that calls initialize.
  • 반대로, 어플리케이션이 적어도 한번 이상 부팅된 적이 있다면, 오브젝트 스토어는 이미 메니지드 오브젝트인 AppListener는 오브젝트 스토어내에 포함되어 있을 것입니다. 따라서, 시스템이 다운되었을 때, 새로운 연결이 시도되었을 때, 이전에 동작했었던 어떤 주기적인 태스크를 동작시키고자 할 때의 실행은 그냥 재시작만 하면 됩니다.
If, on the other hand, the application has been booted at least once, the Object Store will contain the AppListener Managed Object already. In this case, execution just resumes from where it left off when the system came down, listening for new connections and executing any periodic tasks that were running before.

다음 데모 어플리케이션의 의사코드에서 이와 같은 점을 볼 수 있습니다.
In the case of our little demo application, the boot method will have a block in it that, in pseudo-code, looks something like this:


  

initialize {
  

CREATE ROOM MANAGED OBJECT

CREATE SWORD MANAGED OBJECT

ADD REF TO SWORD MANAGED OBJECT TO ROOM’S INVENTORY

SAVE A MANAGED OBJECT REF TO ROOM FOR LATER

}


일반적으로, 처음 실행되는 동안의 매니지드 오브젝트의 초기화를 담당하는 것이 AppListener입니다.
In general, it is the responsibility of the AppListener to create the initial world of Managed Objects during the first startup.


Figure 4: AppListener ManagedObject creates initial ManagedObject world



이제 게임과 같은 윤곽이 드러났습니다. 그렇지만 아직 플레이어 매니지드 오브젝트는 없습니다. 여러분은 AppListener에서와 같은 방법으로 사용자 접속과 같은 플레이어 매니지드 오브젝트를 만들 것입니다. 보통 처음엔 사용자 로그인이 나타납니다. 그리고 그 사용자에 대한 플레이어 오브젝트를 생성시킵니다. 이후 매번 사용자가 로그인할 때 마다 존재하고 있는 해당 플레이어 매니지드 오브젝트를 연결하기만 하면 됩니다. 그러므로 시스템은 필요할 때 플레이어 매니지드 오브젝트를 생성하고 사용자 정보를 기억하고 있습니다.
그렇다면 사용자가 로그인 할 때 어떤 작업을 수행하여야 하겠습니까?
AppListener:loggIn 콜백이 그 해답입니다. 매번 사용자가 PDS 어플리케이션에 로그인할 때, 그 어플리케이션의 AppListener의 loggIn 메소드를 호출하는 태스크가 시작됩니다. AppListener의 loggIn 콜백이 호출된다면 다음 의사코드와 같이 기술된 코드에 따라 실행됩니다.
Now we have something that is beginning to look like our game. We still don’t have Player Managed Objects, however. We will create the Player Managed Objects as users join, in much the same way the AppListener Managed Object was created. The first time we see a user log in, we create a new Player Managed Object for that user. After that, every time that user logs in, we just reconnect him to his existing Player Managed Object. Thus, the system creates Player Managed Objects as needed, and remembers user information between logins.
So how do we find out when a user has logged in?
The answer is the second callback on our AppListener: loggedIn. Every time a user logs into a PDS application, a task is started that calls the loggedIn method on the application’s AppListener. When the loggedIn callback is called on our AppListener, it executes the following code, presented as pseudocode.


loggedIn {
  

managedObject_name = “player_”+ SESSION.PLAYER_NAME;
IF MANAGED OBJECT EXISTS(managedObject_name){
  

FIND MANAGED OBJECT(managedObject_name);

} ELSE {

CREATE NAMED PLAYER MANAGED OBJECT(managedObject_name);

}
SET currentRoom on PLAYER MANAGED OBJECT

TO SAVED MANAGED OBJECT REF TO ROOM

GET ROOM MANAGED OBJECT
ADD PLAYER REF TO ROOM MANAGED OBJECT’S PLAYERS LIST
REGISTER PLAYER MANAGED OBJECT AS SessionListener(SESSION);

}


SessionListener는 또다른 이벤트 인터페이스입니다. 이는 서버로 데이터를 보내는 프로세스나 사용자의 로그아웃과 같은 client API를 사용하는 클라이언트의 액션에 대해 응답하는 태스크상의 get 메소드의 호출되는 메소드를 정의합니다.
SessionListener is another event interface. It defines methods that get called on tasks to respond to actions the client takes with the client API, such as the client sending data to the server for processing and the client logging out.
Figure 5) 매니지드 오브젝트들이 보여지기 시작되는 개요

Figure 5 illustrates that our Managed Object world is starting to look the way we want it to.


Figure 5: Client sends data to server


두번째 사용자가 로그인 할 때 원래 월드가 이전에 있다는 것을 볼 수 있습니다.
When a second user logs in, we will be back to our original world.
Figure 6) 이전 사용자를 포함한 게임상의 재시작 이후의 모형

Figure 6 illustrates our world after restarting the game with our previous players:


Figure 6: AppListener ManagedObject reestablishes simple world


지금까지 의사코드로만 로직이 보여졌습니다. 실행 가능한 코드는 부록A SwordWorld 어플리케이션과 같이 수록되어 있습니다. 실행 가능한 어플리케이션 코드는 간단한 명령어의 구현보다 이상의 것이며 사용자로부터 보내지는 명령들을 어떻게 핸들링해야 하는지를 보여줄 것입니다.
So far, the logic has been laid out in pseudo-code. The actual code to implement this application is included in Appendix A as the SwordWorld application. The actual application code goes a bit further in that it also implements a look command, to show you how the Player Managed Object actually handles commands being sent from the client.



서버 API 클래스 위치시키기 (Locating the Server API Classes)
모든 PDS API 클래스는 com.sun.sgs.app.* 패키지에 포함되어 있습니다.
다음은 Project Darkstar Server API 클래스 상세입니다.
All the Project Darkstar Server API classes are in the com.sun.sgs.app.* package.
These are the Project Darkstar Server API classes with brief descriptions:

System Classes and Interfaces
  

Class
Description
AppContext
해당 어플리케이션에서 가용한 자원 접근의 편의성을 제공합니다. 최초에 레퍼런스를 찾기 위해 매니저가 사용합니다. 이는 시스템에 어플리케이션의 코드를 실행하는 시작점입니다.
Provides access to facilities available in the current application. Primarily used to find references to managers. This is the starting point for the application code to talk to the system.
AppListener
어플리케이션 레벨의 이벤트를 위한 리스너를 제공하는 인터페이스입니다. 이 리스너는 어플리케이션이 최초 실행될 때 그리고 사용자 세션이 로그인 될 때 호출됩니다.
Interface representing a listener for application-level events. This listener is called when the application is started for the first time, and when client sessions log in.
ManagerNotFoundException
요청된 매니저를 찾을 수 없을 때 발생합니다.
Thrown when a requested manager is not found.
ClientSession
사용자와 서버간의 로그인 세션을 위해 하나씩 제공되는 인터페이스입니다.
Interface representing a single, connected login session between a client and the server.
ClientSessionListener
서버에 참여한 클라이언트 세션으로부터 보내지는 메시지를 처리하기 위한 리스너입니다.
Listener for messages sent from an associated client session to the server.



Task Manager Classes and Interfaces
  

Class
Description
TaskManager
스케쥴된 태스크들의 편의성을 제공합니다.
Provides facilities for scheduling tasks.
Task
태스크 매니저에 의해 실행되는 어플리케이션의 동작의 정의입니다.
Defines an application operation that will be run by the Task Manager.
PeriodicTaskHandle
주기적으로 실행되는 태스크 매니저의 태스크 스케줄에 대한 관리를 용이하게 합니다.
Provides facilities for managing a task scheduled with the Task Manager to run periodically.
TaskRejectedException
자원의 제한으로 요청한 태스크를 거부하여 실패된 작업을 스케쥴링할 때 발생합니다.
Thrown when an attempt to schedule a task fails because the Task Manager refuses to accept the task due to resource limitations.
ExceptionRetryStatus
재요청이 필요한 예외가 발생할 때의 동작이 필요한 클래스의 예외 클래스의 구현입니다.
Implemented by exception classes that want to control whether an operation that throws an exception of that exception should be retried.



Data Manager Classes and Interfaces
  

Class
Description
DataManager
공유/퍼시스턴스 오브젝트 접근 관리의 편의성을 제공합니다.
Provides facilities for managing access to shared,persistent objects.
ManagedObject
데이터 매니저에 의해 관리되는 공유/퍼스시턴스 오브젝트와 같은 인터페이스의 구현체입니다.
A marker interface implemented by shared, persistent objects managed by the Data Manager.
ManagedReference
매니지드 오브젝트의 레퍼런스를 제공합니다.
Represents a reference to a managed object.
ObjectIOException
매니지드 오브젝트를 접근시 I/O 오류가 있을 때 발생합니다.
Thrown when an operation fails because of an I/O failure when attempting to access a Managed Object.
ObjectNotFoundException 매니지드 오브젝트를 접근할 때 그 오브젝트를 찾을 수 없을 때 발생합니다.
Thrown when an operation fails because it attempted to refer to a Managed Object that was not found.
TransactionAbortedException
어떤 동작에서 그 때의 트랜션이 시스템에 의해 중지되었을 때 발생합니다.
Thrown when an operation fails because the system aborted the current transaction during the operation.
TransactionConflictException
어떠한 동작이 다른 트랜잭션과 충돌이 일어나서 시스템이 해당 유효한 트랜잭션을 중지시켰을 때 발생합니다.
Thrown when an operation fails because the system aborted the current transaction when it detected a conflict with another transaction.
TransactionException
유효한 트랜잭션이 오류를 일으켰을 때 발생합니다.
Thrown when an operation fails because of a problem with the current transaction.
TransactionNotActiveException
유효하지는 않지만 동작하고 있는 트랜잭션이 오류를 일으켰을 때 발생합니다.
Thrown when an operation fails because there is no current, active transaction.
TransactionTimeoutException
허용된 최대의 트랜잭션 숫자를 넘어서 유효한 동작을 시스템이 중지시켰을 때 발생합니다.
Thrown when an operation fails because the system aborted the current transaction when it exceeded the maximum permitted duration.
NameNotBoundException
바운드되지 않은 오브젝트를 참조하였을 때 발생합니다.
Thrown when an operation fails because it referred to a name that was not bound to an object.



Channel Manager Classes and Interfaces
  

Class
Description
ChannelManager
채널을 획득하거나 생성하기 위한 매니저입니다.
Manager for creating and obtaining channels.
Channel
다중 사용자 세션과 서버간에 존재하는 통신그룹을 제공하는 인터페이스입니다.
Interface representing a communication group, a channel consisting of multiple client sessions and the server.
ChannelListener
한 채널은 ChannelListener에 의해 생성될 수 있으며, ChannelListener는 어떠한 사용자 세션에서 한 채널로 메시지를 보냈을 때 이를 통지합니다. 추가적으로 서버는 한 세션당 한 리스너를 지정할 수 있습니다.(채널에 사용자 세션이 참가하였을 때 채널상의 각 사용자 세션에 의해 보내지는 메제지를 통지할 수도 있습니다.)
A channel can be created with a ChannelListener, which is notified when any client session sends a message on that channel. Additionally, a server can specify a per-session listener (to be notified when messages are sent by an individual client session on a channel when joining a client session to a channel).
Delivery
메세지 전달 요청을 제공합니다. 하나의 전달요청은 하나의 채널을 생성합니다.
Representation for message delivery requirements. A channel is created with a delivery requirement.
NameExistsException
바운드되지 않은 오브젝트를 참조하였을 때 발생합니다.
Thrown when an operation fails because it referred to a name that is currently bound to an object.
NameNotBoundException
ChannelManager.getChannel()로 채널의 이름을 지정할 때 채널이 바운드되지 않는다면 NameNotBoundException이 발생합니다.
Thrown if a channel is not bound to a name specified to ChannelManager.getChannel().






Lesson One: Hello World!
전통적으로 어떠한 튜토리얼이던지 첫번째 예제는 콘솔에 “HelloWorld”를 출력하는 간단한 프로그램입니다. 이는 아무리 간단한 어플리케이션이라도 어플리케이션 로직의 진짜 세상에 도달하기 전 요구되는 추측들을 예상할 수 있게 합니다. 간단하게 시작하기 위하여, 이번 레슨과 다음 두 레슨은 client-server 네트웍을 시작하는 것 보다느 그냥 서버 콘촐에 출력하는 방법을 따릅니다. 클라이언트가 접속되어 있다 하여도, 서버측 로그 메세지는 어플리케이션을 모니터링하는 것과 디버깅에 매우 중요합니다.
It is traditional in any programming tutorial for the first example to be a simple program that prints “HelloWorld” to the console. This lets a programmer see the plumbing required to start even a basic application before diving into real-world application logic. For simplicity, this lesson and the two that follow it print their output on the server console, rather than starting
off with client-server networking. Even when a client is connected, server-side log messages are invaluable for debugging and monitoring the application.

HelloWorld 코딩하기 (Coding HelloWorld)
모든 PDS 어플리케이션은 AppListener와 함께 시작합니다. AppListener는 어플리케이션의 시작과 사용자 로그인 이벤트를 핸들링하는 객체입니다. 어플리케이션의 AppListener는 AppListener 인터페이스의 간단한 구현 클래스입니다. AppListener 또한 매니지드 오브젝트이지만 AppListener는 마커 인터페이스인 Serializable 인터페이스를 반드시 상속하여야 합니다.
위에 언급한 내용에 따르면, AppListener는 두 메소드를 포함합니다. initialize와 loggedIn 입니다. 어플리케이션의 오브젝트 스토어가 비어있든 그렇지 않든 initialize 메소드는 어플리케이션이 처음 시작할 때 호출됩니다. 어플리케이션의 오브젝트 스토어가 삭제되거나 시스템이 깨끗한 “전혀 실행된 적이 없는” 상태가 아닌 이상, 관례적으로 이 뜻은 어플리케이션 하나당 한번 생성된다는 뜻입니다.
HelloWorld AppListener는 다음과 같습니다.
All PDS applications start with an AppListener. An AppListener is the object that handles an application’s startup and client login events. An application’s AppListener is simply a class that implements the AppListener interface. Since an AppListener is also a Managed Object, it must implement the Serializable marker interface as well.
As mentioned above, AppListener contains two methods: initialize and loggedIn. The initialize method gets called on the startup of the application if and only if the Object Store for this application is empty. The AppListener is automatically created in the Object Store by the system the first time the application is started up; in practice, this means that it is created once per application, unless the Object Store for this application is deleted and the system is returned to its pristine “never having run this application” state. 6
A “Hello World” AppListener looks like this:

HelloWorld
/*
* Copyright 2007 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 3 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson1;
import java.io.Serializable;
import java.util.Properties;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
/**
* Hello World example for the Project Darkstar Server.
* Prints {@code “Hello World!”} to the console the first time it’s
* started.
*/
public class HelloWorld implements AppListener, // to get called during application startup.
Serializable // since all AppListeners are ManagedObjects.
{
  

/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;

/**
* {@inheritDoc}
* <p>
* Prints our well-known greeting during application startup.
*/
public void initialize(Properties props) {
  

System.out.println(“Hello World!”);

}

/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {

return null;

}

}



HelloWorld 실행하기 (Running HelloWorld)
HelloWorld를 실행하려면 다음과 같은 것이 필요합니다.
To run HelloWorld you need the following:

  • 여러분의 시스템에 Project Darkstar Server가 설치되어야 합니다.
  • The Project Darkstar Server installed on your system.
  • 1.5.0_11 이상의 JDK™ 5 가 설치되어야 합니다. 설치된 버전은 다음 명령으로 확인할 수 있습니다.
  • A JDK™ 5 installation, version 1.5.0_11 or better. The version can be found with:
java -version
path에 자바가 걸려있지 않다면 JAVA_HOME에 자바가 설치된 경로의 root를 설정하여야 합니다.
If java is not in your default execution path, you will need to set JAVA_HOME to point to the root of its installation on your system.

  

Path 규칙(Path Conventions)

Unix 또는 Unix와 같은 시스템은 디렉토리의 경로를 슬래쉬(/)로 구분합니다. 그러나 Win32에서는 역슬래쉬(\)로 표기합니다. 이 문서는 특별히 윈도우즈의 표기법을 설명하지 않는 한 Unix의 표기법을 따릅니다.
여러분이 윈도우즈에서 작업한다면 슬래쉬로 표기된 경로를 바꾸어야 한다는 것을 기억하십시오.
Unix and many Unix-derived systems use a forward slash (/) to show subdirectories in a file path. Win32, however, uses a backslash (\) for this purpose. Throughout this document we use the Unix convention for file paths unless it is in a Windows-specific example.
Please remember that you may have to substitute backslashes for forward slashes in the generic examples if you are working in Windows.


튜토리얼의 폴더를 보시면 컴파일된 tutorial.jar 파일에서 모든 튜토리얼의 예제를 찾을 수 있습니다. 모든 각각의 예제는 오브젝트 스토어의 데이터를 포함한 데이터 서브디렉토리가 포함되어 있습니다.
The tutorial.jar file in the tutorial folder contains pre-compiled .class files for all the tutorial examples. The data directory contains subdirectories for the object store data for all the different examples.

여러분의 컴퓨터에 PDS가 인스톨되어 있는 루트 디록토리에서 sgs 스크립트를 실행하여 튜토리얼 예제를 실행할 수 있습니다.
You run a tutorial example by using the sgs script in the root of the PDS installation on your computer. It has the following form:

  • For Unix:
sgs.sh app_classpath app_config_file …
  • For Windows:
sgs app_classpath app_config_file …

app_classpath는 어플리케이션 클래스들이 위치하고 있는 디폴트 경로이고, app_config_file은 실행하고자 하는 각 어플리케이션의 설정파일입니다.(이 튜토리얼에서는 일반적으로 한번에 한번 실행할 것입니다.) 설정된 경로에 기본 설정 파일이 제공되어 있을 것입니다. 해당 경로는 SDK가 설치된 경로의 tutorial 폴더에 있습니다. 이제 설정된 경로에서 HelloWorld를 실행하기위해 다음을 따르십시오.
Where app_classpath is a default classpath in which to find the application classes, and app_config_file … are configuration files for each application you want to launch. (In this tutorial you will generally only launch one at a time.)
We have provided default configuration files that use relative paths. These paths assume your working directory is the tutorial folder in the SDK. So, to run HelloWorld as it is shipped to you in the tutorial directories, do the following:

  1. PDS가 설치되어 있는 경로로 SGSHOME 환경변수를 추가합니다. Add the environment variable SGSHOME to your environment and set it to the directory where you installed the Project Darkstar Server.
  2. SGSHOME 환경변수를 path에 추가합니다. Add SGSHOME to your execution path, where SGSHOME is your PDS install directory.
  3. 쉘을 엽니다. Open a Unix shell, a Windows command window, or whatever you do to get a command line on your development system.
  4. 튜토리얼 디렉토리로 이동합니다. 유닉스 환경이라면 다음과 같이 명령을 낼 것입니다. Change your working directory to the tutorial directory of your PDS. In Unix, the command might be something like this:
cd ~/sgs/tutorial
  1. 다음을 타이핑 하십시오. Type the following:
  • For Unix:
sgs.sh tutorial.jar HelloWorld.properties
  • For Windows:
sgs tutorial.jar HelloWorld.properties
  1. Add the environment variable SGSHOME to your environment and
    set it to the directory where you installed the Project Darkstar Server.
  2. Add SGSHOME to your execution path, where SGSHOME is your PDS install directory.
  3. Open a Unix shell, a Windows command window, or whatever you do to get a command line on your development system.
  4. Change your working directory to the tutorial directory of your PDS. In Unix, the command might be something like this:
cd ~/sgs/tutorial
  1. Type the following:
  • For Unix:
sgs.sh tutorial.jar HelloWorld.properties
  • For Windows:
sgs tutorial.jar HelloWorld.properties

여러분은 “HelloWorld!”라는 출력을 표준출력으로 볼 수 있을 것이고 이후는 동작하지 않을 것입니다. 이 시점에서 여러분은 어플리케이션을 강제종료할 수 있읍니다. 유닉스 또는 윈도우즈 환경에서 Ctrl-c를 눌러 서버를 종료하고 프롬프트로 돌아갈 수 있습니다. 구미가 당긴다면 PDS 어플리케이션의 시작 스크립트인 sgs.bat 또는 sgs.sh를 시험해볼 수 있고, PDS에서 어플리케이션 설정이 어떻게 되어 있는지에 대한 상세한 내용을 HelloWorld.properties 파일을 통하여 볼 수 있습니다.
You should see the application print out “Hello World!” to standard output (as well as a couple of PDS startup log messages) and then sit doing nothing. At this point you can kill the application; in Unix or Win32, just type Ctrl-c in the shell window to stop the server and get the prompt back. If you are interested, you can examine the script files sgs.bat and sgs.sh to see how to run PDS applications, and the HelloWorld.properties file to see the details of how you set up an application configuration to run in the PDS.

(역자주:PDS 어플리케이션이 동작하고 개발환경을 설정하기 위해, 또 PDS 소스로부터 sgs.jar로 컴파일하려고 한동안 삽질했었습니다. 또 다른 뉴비가 삽질하지 않도록 실행, 디버그 환경을 설정하는 방법을 남깁니다.)

개발 OS는 Windows XP이고, JDK 1.6, eclipse 3.2 입니다.
   

  1. CLASSPATH가 제대로 되어 있는지 확인하세요.
  2. eclipse가 제대로 구동되는지 확인하세요.
  3. Project Darkstar Server 바이너리(sgs-0.9.5.1-r3730.zip)를 다운로드하고 압축을 풀면 다음과 같은 디렉토리를 볼 수 있습니다.
    1. bdb-4.5.20
    2. mina-1.1
    3. sgs-0.9.5.1-r3730
    4. slf4j-1.4.0
  4. sgs-0.9.5.1-r3730 디렉토리를 eclipse의 workplace에 복사하고 darkstar라고 디렉토리명을 바꾸세요.
  5. bdb-4.5.20 디렉토리의 이름을 bdb로 변경하고 darkstar\lib에 bdb 디렉토리를 통채로 복사하세요. 그러면 darkstar\lib\bdbdb.jar, darkstar\lib\bdb\win32-x86 와 같이 디렉토리, 파일이 설정될 겁니다.
  6. 환경변수에 SGSHOME을 설정하세요, 제 경우는 D:\src\workplace\darkstar 이렇게 되었습니다.
  7. PATH에도 SGSHOME을 추가해주세요.
  8. eclipse를 실행하시고 새 프로젝트를 만듭니다. 이클립스의 메뉴에 File -> New -> Java Project 를 선택하시면 됩니다.
  9. Content 박스에 Create project from existing source를 선택하시고, Browse 버튼을 클릭하여 SGSHOME로 설정한 PDS를 설치한 경로를 선택하세요.
  10. Project Name은 반드시 darkstar가 되어야 할 겁니다. 그렇게하고 Finish 버튼을 눌러 프로젝트를 만들면 알아서 lib등의 경로를 검색하여 프로젝트를 구성할 겁니다.
  11. SGSHOME 디렉토리에 새 파일을 만들어 이름을 Main.launch라고 지정합니다.
  12. Main.luanch을 텍스트 에디터로 열어 http://www.projectdarkstar.com/wiki/doku.php/darkstar.launch 의 내용을 기록하고 저장하세요.
  13. 이클립스의 Package Editor를 리프레쉬(F5)하면 Main.luanch 파일이 추가되어 있을 것이고 Run 버튼에 Main 메뉴가 추가되었을 것입니다.
  14. Run 버튼을 누르면 파일을 선택할 다이얼로그가 보일 것인데, *.properties 파일을 선택하시면 PDS가 실행될 것입니다.
    1. 여기서 잠깐! .properties 파일을 열어보면 com.sun.sgs.app.root=data/HelloWorld 라고 적혀 있는것을 볼 수 있습니다.
    2. SGSHOME은 D:\src\workplace\darkstar 이라고 설정되었을때 어플리케이션의 루트(com.sun.sgs.app.root)는 D:\src\workplace\darkstar이고 튜토리얼의 .properties에 기술된 대로 실행된다면 D:\src\workplace\darkstar\data\HelloWorld에서 어플리케이션을 실행하려고 하여 에러가 뜹니다.
    3. 정상적으로 실행하려면 .properties의 com.sun.sgs.app.root=data/HelloWorld를 com.sun.sgs.app.root=tutorial/data/HelloWorld로 바꾸시거나 tutorial의 모든 파일과 디렉토리를 SGSHOME에 복사하셔야 합니다. 전 전자를 택했습니다.







HelloWorld 재시작하기 (Rerunning HelloWorld)
PDS를 중지하고 HelloWorld 어플리케이션을 다시 동작시키려 한다면, “HelloWorld!” 출력이 나오질 않고 에러를 보게 될 것입니다. 이는 오브젝트 스토어에 이전에 동작하고 있던 AppListener가 이미 존재하므로 호출되지 않았던 초기화를 하여야 합니다.
HelloWorld를 다시 동작하고 싶다면, 여러분은 오브젝트 스토어의 내용을 지워야 합니다
  

  • For Unix:
rm -r data/HelloWorld/dsdb/*
  • For Windows:
del /s data\HelloWorld\dsdb\*.*

(역자주 : 저도 지우려고 했는데 잘 지워지지 않더군요. JVM을 Kill해도 dll로 호출된(bds)것들이 아직 메모리상에서 동작하기 때문에 종료할 때 까지 기다려줘야 합니다. Darkstar 포럼에서는 10초 이후 파일을 지울 수 있다고 합니다만, PDS 소스를 확인해보는 수 밖에 없습니다.)
If you stop the PDS and then run the HelloWorld application again, you will notice that you don’t get a “HelloWorld!” output the second time. This is because the AppListener already exists in the Object Store from the previous run, and thus the initialize method on it is never called.
If you want to see “Hello World” again, you can do it by clearing the Object Store with the following commands:

  • For Unix:
rm -r data/HelloWorld/dsdb/*
  • For Windows:
del /s data\HelloWorld\dsdb\*.*




Lesson Two: Hello Logger!

HelloLogger 코딩하기 (Coding HelloLogger)

PDS는 java.util.logging.* 패키지의 자바 로깅 메커니즘 표준을 지원합니다. 이는 유연하고 설정하기 쉬운 로깅 API이며 자바로 작성된 대부분의 서버에서 사용됩니다. PDS 서버는 다양한 내부 상태와 이벤트를 기록하기 위하여 스스로 로거를 사용합니다.
아래에 표준출력 보다는 로거로 “Hello World!”를 보내는 HelloWorld 프로그램이 재작성되어 있습니다.
The PDS supports the standard Java logging mechanisms in the java.util.logging.* package. This is a flexible and configurable logging API used by most servers written in Java. The PDS server itself uses the logger to report various internal states and events. It is highly recommended that applications use the same logging mechanisms for their reporting.
Below is a rewrite of HelloWorld that sends the “Hello World!” string to the logger rather than to standard out:
HelloLogger
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson2;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
/**
* Hello World with Logging example for the Project Darkstar Server.
* It logs {@code “Hello World!”} at level {@link Level#INFO INFO}
* when first started.
*/
public class HelloLogger

implements AppListener, // to get called during application startup.

Serializable // since all AppListeners are ManagedObjects.

{

/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger = Logger.getLogger(HelloLogger.class.getName());
/**
* {@inheritDoc}
* <p>
* Logs our well-known greeting during application startup.
*/
public void initialize(Properties props) {

logger.log(Level.INFO, “Hello World!”);
 

}
/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {

return null;
 

}

}
로깅 속성 파일(The Logging Properties File)
자바 로깅 API는 엄격한 메세지 레벨로 고려되어 있습니다. 우리가 로깅하려는 엄격한 info level에서의 메세지 시스템이 Level.INFO에 설명됩니다.
로깅 속성 파일에 의해 자바 로거가 동작합니다. 여러분이 어플리ㅔ이션을 동작하시키기 위하여 사용하는 sgs-boot.jar 파일은 PDS가 설치된 conf 디렉토리상에 제공되는 sgs-logging.properties 파일을 사용합니다. 이는 PDS 프로세스의 자바 커맨드 라인의 java.util.logging.config.file의 속성을 셋팅하여 완성됩니다.
이 파일의 디폴트 레벨은 info입니다. 이는 “fine”과 같은 디버깅 메세지 등의 info 레벨의 아래에 있는 로깅 메세지는 출력되지 않다는 것을 뜻합니다. 이 파일을 설정하는 더 자세한 설명은 JDK™ 6 API documentation을 참조하시기 바랍니다.

The Logging Properties File

The Java logging API has a concept of message severity level. By logging at Level.INFO, we are telling the system we want to log this message at the info level of severity.
The Java logger’s behavior is controlled by a logging properties file. The sgs-boot.jar file you use to run your applications uses the file sgs-logging.properties that is present in the conf directory of your PDS installation.
This is accomplished by setting the java.util.logging.config.file property on the java command line of the PDS process.
By default this file sets the logging level to info. This means that logging messages below the level of info, such as “fine” debugging messages, will not be printed. You can change this by editing the sgs-logging.properties file. For more information on how to edit this file, please see the JDK™ 6 API documentation.
Lesson 3: Tasks, Managers, and Hello Timer!
태스크(Tasks)
Lesson 1, 2에서 PDS 시스템은 이벤트의 응답(이 케이스에서는 새로 설치된 PDS 어플리케이션의 초기화 동작)으로 초기화 메소드를 자동으로 호출하였습니다. 이는 PDS 환경과 함께 실행되는 것을 뜻합니다.
PDS 환경에서 모든 코드의 실행은 태스크의 한 부분이 되어야 합니다. 어플리케이션 개발자 관점에서 보면, 태스크는 단일 스레드이고, 트랜잭션으로 동작하며 이벤트 모델의 코드입니다. 이는 태스가 단지 한순간 실행되는 태스크라는 것과 전체가 아니면 아무것도 실행되지 않는 것과 같이 데이터의 변경이 종료되는 것을 뜻합니다.
In Lessons 1 and 2, the system automatically invoked the initialize method for us in response to an event (in this case, the initial run of a newly installed PDS application). This meant executing code within the PDS environment.
All code run in the PDS environment must be part of a task. From the point of view of the application coder, a task is a piece of monothreaded, transactional, event-driven code. This means that the task runs as if it were the only task executing at that moment, and all actions done by the task to change data occur in an all-or-nothing manner.
태스크 실행의 실체(The realities of task execution)
각각의 태스크는 단일 스레드로 실행됩니다. 그러나 우리가 차례대로 실행하지 않는 다면, 한 태스크가 각각의 태스크를 이전에 끝나는 것과 다음 시작하는 것을 기다릴때, 그 태스크는 PDS 제공자를 조정되지 못할 것입니다.
그 대신에 PDS는 수많은 단일 스레드 태스크를 동시에 실행하고 각 매니지드 오브젝트의 경쟁 상태를 주시합니다. 두 매니지드 오브젝트가 경쟁하고 있다면, 한 태스크는 멈추게 되고 다른 태스크가 끝나기를 기다릴 것입니다.
많은 태스크가 경쟁상태가 아닌 상태로 같은 시간에 같은 매니지드 오브젝트로 읽혀질 수 있습니다. 만약 어떤 태스크가 매니지드 오브젝트로 사용되어야 한다면 경쟁상태가 되더라도 같은 매니지드 오브젝트로 읽혀지거나 사용됩니다.
최적화된 성능을 위해, 데이터 구조와 가능한한 잠재적 오프젝트 경쟁을 포함한 게임 로직을 디자인 하는 것은 중요합니다. 동시에 발생하는 그리고 똑같은 매니지드 오브젝트로 쓰여져야만 할 지도 모르는 다중 태스크의 공간을 효율적으로 또 세심하게 설계하시기 바랍니다. 모든 태스크는 run 메소드를 갖고 있는 Task 인터페이스를 PDS 스캐쥴러에 등록됩니다.
태스크는 가능한한 빨리 또는 최소 지연시간 이후에 실행되기 위하여 스캐줄러에 등록됩니다. 태스크는 한번 실행되고 끝나거나 반복될 수 있습니다. 반복되는 태스크라면 반복 주기가 설정되어 있을 것입니다.
반복되는 태스크는 전통적인 게임 시스템에서의 “timer” 또는 “heartbeat”와 같습니다. 이는 여러분이 밀리세컨드 단위로 설정된 이벤트를 효율적으로 생성할 수 있게 합니다.
Each individual task executes in a monothreaded manner. However, if we executed them serially,
waiting for each one to finish before the next one started, it would not be possible to get the kind of scaling the PDS provides.
Instead, the PDS executes many of these monothreaded tasks simultaneously and watches for
contention on the individual Managed Objects. If two tasks contend for control over a Managed
Object, one task will be held up and will wait for the other to finish before it can proceed.
Many tasks can read the state of the same Managed Object at the same time without causing
contention. If any of them wants to write to it, however, that can cause contention with tasks that read from or write to the same Managed Object.
To achieve optimal performance, it is important to design your data structures and game logic
with as little potential object-contention as possible. Be especially wary of places where multiple tasks that are likely to occur simultaneously might have to write to the same Managed Object.
All tasks registered with the PDS scheduler implement the interface Task, which has one method on it . run.6
A task may be submitted to the scheduler to be executed either as soon as possible, or after a minimum delay time. A task can be one-shot or repeating. If it is a repeating task, it is also submitted with a period of repeat.
A repeating task is the same thing as a “timer” or “heartbeat” in traditional game systems; it lets you effectively generate an event to be handled every specified number of milliseconds.
매니저(Managers)
서버 어플리케이션의 게임로직과 외부와의 모든 통신은 매니저를 통하여 완료됩니다. 아래에 세가지 타입의 매니저가 기술되어 있습니다.

● 태스크 매니저 Task Manager
● 데이터 매니저 Data Manager
● 채널 내니저 Channel Manager

인스톨 특화 매니저가 있을 수 있습니다. 이는 다른 서버 어플리케이션의 개발자에 의해 작성되고 PDS의 백엔드로 배포된 것일 수 있습니다. AppContext 클래스상의 고정 호출 흐름은 매니저와 통신하는 서버 어플리케이션 코드에서 사용됩니다. AppListener와 같은 이벤트 핸들링 인터페이스가 태스크를 구현하지 않더라도, 이 리스너는 일반 PDS 어플리케이션 코드와 같이 내부 태스크로 호출 될 수 있습니다.

● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)

이 튜토리얼은 처음의 세가지를 포함합니다. 그 마지막은 인스톨 특화 매니저의 호출이며 이는 다음 버전의 Project Darkstar Server Extension Manual에 포함될 것입니다.
IMPORTANT: 매니저 레퍼런스는 호출된 매니저안의 태스크 전에에 대한 유일한 검증입니다. 그러므로 여러분은 AppContext로부터 호출하는 것 대신에 외부로부터 매니저를 참조하는 것을 시도하지 않는 것이 좋습니다.
태스크 매니저는 스캐줄러를 포함하는 PDS의 한 부분이고 우리가 사용할 스캐쥴 태스크입니다. 실행된 태스크와 같이 태스크 매니저 내에 스캐쥴링 되기 위하여, 오브젝트는 반드시 직렬화되어야 하며 Task 인터페이스를 상속하여야 합니다. 0.5초 주기를 갖고 5초 딜레이 이후 “Hello Timer” 메세지를 로깅하는 AppListener의 예제가 하단에 있습니다.
IMPORTANT: PDS 스택이 스케쥴링에서 태스크를 실행하기 위하여 가장 많은 노력을 기울이는 동안, 과부하가 걸린 태스크가 반복 실행되는 동안 한 태스크가 밀려날 수 있습니다. 이런 상황에서 해당 태스크는 그 기간에 실행을 스킵할 것이고 다음 스캐쥴링에서 실행될 것입니다. 추가로 매니지도 오브젝트의 경쟁은 지연된 태스크의 원인이 될 수도 있습니다.
스캐쥴링된 태스크의 요청된 반복 주기는 복잡한 그래픽일 수록 낮은 수로 실행되는 게임 클라이언트의 목표 프레임율과 비슷합니다. 여러분의 어플리케이션 로직이 일정 시간내에 실행되지 않을 정도로 타이트하다면 여러분은 설정된 시간과 그 실행 로직에서 스킵된 부분을 점검하고 핸들링할 것입니다.
All communication between your server application’s game logic and the world outside of it is accomplished through managers. As described above, there are three standard managers:

● Task Manager
● Data Manager
● Channel Manager

There can also be installation-specific managers; these can be written by the author of the server application and deployed with the application into a PDS back end. The following static calls on the AppContext class are used by server application code to get a reference to a manager to talk to:
Although the event-handling interfaces like AppListener do not implement Task, these listeners get called from an internal Task just like regular PDS application code.

● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)

The first three are covered in this tutorial. The last is a generic call to get an installation-specific manager; it will be covered in the forthcoming Project Darkstar Server Extension Manual.
IMPORTANT: A Manager Reference is valid only for the life of the task within which the get manager call was invoked. Therefore, you should not try to cache manager references for use outside of that one invocation chain; get them from the AppContext instead.
The Task Manager is the part of the PDS that contains the scheduler, and thus what we use to schedule tasks. In order to be scheduled with the Task Manager as a task to be run, an object must be serializable and must implement the Task interface.7 The example below turns our AppListener into a task and starts it logging “Hello Timer” messages after a five-second delay at a half-second repeat period.
IMPORTANT: While the PDS stack makes a best effort to run tasks on schedule, it may back off execution of repeating tasks under heavy load. In that case, the task will skip execution of this period and reschedule to the next period. Additionally, contention for Managed Objects may cause a timed task to delay its execution.
The requested repeat frequency of a timed task is similar to a target frame rate in a game client, where frames may be dropped if they are taking too long to compute. If your application logic is tied to elapsed time or absolute number of “beats” in a given period of time, you’ll need to check the elapsed time and handle skipped periods in your run logic.
HelloTimer 코딩하기(Coding HelloTimer)
아래의 HelloTimer 어플리케이션은 반복되는 태스크를 스케쥴링하기 위하여 TaskManager를 사용합니다. 해당 태스크는 500ms의 주기로 5000ms 지연 이후에 실행될 것입니다.
The HelloTimer application below uses the TaskManager to schedule a repeating task. The task will run after a delay of 5000ms at a frequency of once every 500ms.
HelloTimer
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson3;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.Task;
import com.sun.sgs.app.TaskManager;
/**
* A simple timed-task example for the Project Darkstar Server.
* It uses the {@link TaskManager} to schedule itself as a periodic task
* that logs the current timestamp on each execution.
*/
public class HelloTimer

implements AppListener, // to get called during application startup.
Serializable, // since all AppListeners are ManagedObjects.
Task // to schedule future calls to our run() method.

{

/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloTimer.class.getName());
/** The delay before the first run of the task. */
public static final int DELAY_MS = 5000;
/** The time to wait before repeating the task. */
public static final int PERIOD_MS = 500;
// implement AppListener

/**
* {@inheritDoc}
* <p>
* Schedules the {@code run()} method to be called periodically.
* Since SGS tasks are persistent, the scheduling only needs to
* be done the first time the application is started. When the
* server is killed and restarted, the scheduled timer task will
* continue ticking.
* <p>
* Runs the task {@value #DELAY_MS} ms from now,
* repeating every {@value #PERIOD_MS} ms.
*/
public void initialize(Properties props) {

TaskManager taskManager = AppContext.getTaskManager();
taskManager.schedulePeriodicTask(this, DELAY_MS, PERIOD_MS);
 

}

/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {

return null;
 

}
// implement Task

/**
* {@inheritDoc}
* <p>
* Logs the current timestamp whenever this {@code Task} gets run.
*/
public void run() throws Exception {

logger.log(Level.INFO,
 

“HelloTimer task: running at timestamp {0,number,#}”,
System.currentTimeMillis());
 

}

}
이제 반복 이벤트를 만들었고 우리는 정지되고 재시작할 때 어떤 동작을 일으키는 첫번째 어플리케이션을 만들었습니다. 태스크 등록은 지속적이어야 합니다. 이는 다운되거나 재시동할 때 상태를 지속할 수 있습니다. 이 동작을 살펴보기 위해 서버를 정지시키고 재시작해보십시오.
주기적인 태스크는 오브젝트 스토어 내에 여러분의 오브젝트와 함께 저장된 정보입니다. 따라서 여러분이 데이터 디렉토리를 삭제하고 초기화되어있지 않은 처음의 상태로 되돌린다면 주기적인 태스크 또한 초기화될 것입니다.
이 행동은 여러분이 서버가 항상 동작하는 것 처럼 여러분의 코드를 작성하는 것을 허용하며, 여러분이 중요한 로직을 포함한 주기적 태스크의 동작 메소드 내의 경과된 시간을 점검하는 것에 주의를 요합니다.
어떻게 마지막 실행 시간 추적을 유지하는냐에대한 주제가 다음 레슨의 주제입니다.
Now that we have a repeating event, we have our first application that will do something when stopped and restarted. Task registration is persistent, which is to say, it survives a crash and reboot. Try stopping the server and restarting it again to see this in action.
The periodic task is information stored in the Object Store along with your managed objects, so if you clear the data directory and return it to its pristine, uninitialized state, the periodic tasks will also get cleared.
This behavior allows you to write your code as if the server were always up, with the caveat that you do have to check elapsed time in your periodic task’s run method if a delay between that and the last time it was run has significance to your logic.
How you keep track of the last time run was called is the subject of the next lesson.
Tutorial Lesson 4: Hello Persistence!
레슨 3에서 요청이 있을 때 딜레이 상태 또는 반복상태로 동작하는 태스크를 설명하였습니다. 이는 시스템이 과부하가 걸리거나 전체 데이터 센터가 실제로 셧다운되거나 시작될 때입니다.
호출된 마지막 실행 시간을 추적하고 실제 델타-시간을 계산하기 위하여, 시스템이 다운될 때 지속되기 바라기 때문에 우리는 지나간 시간값을 저장하는 방법이 필요합니다. 이것은 persistent storage라고 불리고 실제 게임상에서 매우 중요한 것입니다. 여러분의 머신이 다운되고 모든 캐릭터와 그 모든 것들이 날아갈 때 유저들의 반응이 어떨지 상상해 보십시오.
매니지드 오프젝트는 시스템이 상태를 추적하고 영속성을 가질 수 있기 위한 오브젝트입니다. 우리는 이전에 AppListener 이터페이스가 매니지드 오브젝트를 상속하였으며 시스템에 의하여 자동적으로 생성되는 것을 언급하였습니다. 시스템은 또한 데이터 매니저같은 매니지드 오브젝트처럼 여러분의 AppListener를 등록합니다. 이는 매니지드 오브젝트의 상태가 PDS에 의해 제공됨을 의미합니다.
Lesson 3 explained that tasks that are run on a delay or repeat don’t necessarily happen exactly at the time you asked for. They could happen a bit later if (for example) the system is very loaded, or a lot later if (for example) the entire data center has actually come down and had to be restarted.
To track the last time the run task was called and calculate the true time-delta, we need a way of storing the past time value so that it will survive the system going down. This is called persistent storage, and in real games it is very important. Imagine how your users would react if your machine went down and they all lost their characters and everything on them!
A Managed Object is an object for which the system tracks state and which the system makes persistent. We mentioned above that AppListener interface inherits the Managed Object interface and that your AppListener instance is automatically created by the system for you. The system also registers your AppListener as a Managed Object with the Data Manager. This means that its state will be preserved by the PDS for you.
HelloPersistence 코딩하기 (Coding HelloPersistence)
HelloTimer 태스크가 매니지드 오브젝트인 것 처럼, 우리가 하기 원하는 모든 것은 호출된 마지막 실행 시간을 추적하는 필드를 추가하는 것입니다. 다음은 HelloPersistence의 코드입니다.
PDS 어플리케이션처럼 HelloPersistence를 실행하십시오. PDS를 정지시키고, 1분 쯤 기다린 후 다시 시작시키십시오. 여러분은 다운된 시간을 포함한 경과된 시간을 보게 될 것입니다. 이는 시스템 시간을 기초로한 currentTimeMillis 와, PDS가 실행되고 있지 않았을 때 지나간 시간때문입니다.
영속화는 PDS상에서 간단하고 자동으로 동작합니다. 등록된 매니지드 오브젝트의 어떤 일시적이지 않은 필드는 영속화될 것입니다.
Since our HelloTimer task is a Managed Object, all we need to do is add a field to track the last time run was called. Below is the code for HelloPersistence.
Run HelloPersistence as a PDS application. Stop the PDS, wait a minute, and then start it again. You will see that the elapsed time reported includes the down time. This is because currentTimeMillis is based on the system clock, and time kept moving forward even when the PDS wasn’t running.
Persistence is that simple and automatic in the PDS. Any non-transient field on a registered Managed Object will be persisted.10
HelloPersistence
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson4;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.Task;
import com.sun.sgs.app.TaskManager;
/**
* A simple persistence example for the Project Darkstar Server.
* As a {@link ManagedObject}, it is able to modify instance fields,
* demonstrated here by tracking the last timestamp at which a task
* was run and displaying the time delta.
*/
public class HelloPersistence  

implements AppListener, // to get called during application startup.
Serializable, // since all AppListeners are ManagedObjects.
Task // to schedule future calls to our run() method.
 

{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloPersistence.class.getName());
/** The delay before the first run of the task. */
public static final int DELAY_MS = 5000;
/** The time to wait before repeating the task. */
public static final int PERIOD_MS = 500;
/** The timestamp when this task was last run. */
private long lastTimestamp = System.currentTimeMillis();
// implement AppListener
/**
* {@inheritDoc}
* <p>
* Schedules the {@code run()} method to be called periodically.
* Since SGS tasks are persistent, the scheduling only needs to
* be done the first time the application is started. When the
* server is killed and restarted, the scheduled timer task will
* continue ticking.
* <p>
* Runs the task {@value #DELAY_MS} ms from now,
* repeating every {@value #PERIOD_MS} ms.
*/
public void initialize(Properties props) {

TaskManager taskManager = AppContext.getTaskManager();
taskManager.schedulePeriodicTask(this, DELAY_MS, PERIOD_MS);
 

}
/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {

return null;
 

}
// implement Task
/**
* {@inheritDoc}
* <p>
* Each time this {@code Task} is run, logs the current timestamp and
* the delta from the timestamp of the previous run.
*/
public void run() throws Exception {

long timestamp = System.currentTimeMillis();
long delta = timestamp – lastTimestamp;
// Update the field holding the most recent timestamp.
lastTimestamp = timestamp;
logger.log(Level.INFO,
 

“timestamp = {0,number,#}, delta = {1,number,#}”,
new Object[] { timestamp, delta }
);
 

}

}
Coding HelloPersistence2While we could put all the fields of our application on the AppListener, there are many good reasons not to do this. As any Managed Object grows larger, it takes more time for the system to store and retrieve it. Also, although PDS task code is written as if it were monothreaded, many tasks are actually executing in parallel at any given time. Should the tasks conflict in what data they have to modify, then one will either have to wait for the other to finish or, in a worst-case situation, actually abandon all the work it had done up to that point and try again later.
For these reasons, an application will want to create other Managed Objects of its own. Luckily, that’s easy to do!
All Managed Objects must meet two criteria:
● They must be Serializable.
● They must implement the ManagedObject marker interface. (AppListener actually inherits the
ManagedObject marker interface for you.)
One good way to break your application up into multiple Managed Objects is by the events they handle. A Managed Object can handle only one event at a time, so you want to separate all event handlers for events that might occur in parallel into separate Managed Objects. Below is the code to HelloPersistence2. It creates a separate TrivialTimedTask Managed Object from the AppListener to handle the timed task.
HelloPersistence2
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson4;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.TaskManager;
/**
* A simple persistence example for the Project Darkstar Server.
*/
public class HelloPersistence2
implements AppListener, Serializable
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloPersistence2.class.getName());
/** The delay before the first run of the task. */
public static final int DELAY_MS = 5000;
/** The time to wait before repeating the task. */
public static final int PERIOD_MS = 500;
// implement AppListener
/**
* {@inheritDoc}
* <p>
* Creates a {@link TrivialTimedTask} and schedules its {@code run()}
* method to be called periodically.
* <p>
* Since SGS tasks are persistent, the scheduling only needs to
* be done the first time the application is started. When the
26 12/19/08 Project Darkstar Server Application Tutorial
* server is killed and restarted, the scheduled timer task will
* continue ticking.
* <p>
* Runs the task {@value #DELAY_MS} ms from now,
* repeating every {@value #PERIOD_MS} ms.
*/
public void initialize(Properties props) {
TrivialTimedTask task = new TrivialTimedTask();
logger.log(Level.INFO, “Created task: {0}”, task);
TaskManager taskManager = AppContext.getTaskManager();
taskManager.schedulePeriodicTask(task, DELAY_MS, PERIOD_MS);
}
/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {
return null;
}
}
TrivialTimedTask
This is the Managed Object we are going to have respond to the repeating task.
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson4;
import com.sun.sgs.app.AppContext;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.ManagedObject;
import com.sun.sgs.app.Task;
/**
* A simple repeating Task that tracks and prints the time since it was
* last run.
Project Darkstar Server Application Tutorial 12/19/08 27
*/
public class TrivialTimedTask
implements Serializable, // for persistence, as required by ManagedObject.
ManagedObject, // to let the SGS manage our persistence.
Task // to schedule future calls to our run() method.
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(TrivialTimedTask.class.getName());
/** The timestamp when this task was last run. */
private long lastTimestamp = System.currentTimeMillis();
// implement Task
/**
* {@inheritDoc}
* <p>
* Each time this {@code Task} is run, logs the current timestamp and
* the delta from the timestamp of the previous run.
*/
public void run() throws Exception {
// We will be modifying this object.
AppContext.getDataManager().markForUpdate(this);
long timestamp = System.currentTimeMillis();
long delta = timestamp – lastTimestamp;
// Update the field holding the most recent timestamp.
lastTimestamp = timestamp;
logger.log(Level.INFO,
“timestamp = {0,number,#}, delta = {1,number,#}”,
new Object[] { timestamp, delta }
);
}
}
Coding HelloPersistence3
A Managed Object does not actually become managed by the Data Manager, and thus persistent, until the Data Manager is made aware of it. The reason HelloPersistence2 works is because the Task Manager persisted the TrivialTimedTask object for us. In order to persist other Managed Objects, though, an application needs to take on the responsibility of informing the Data Manager itself. One way the Data Manager can become aware of a Managed Object is through a request for a Managed Reference.
Managed Objects often need to refer to other Managed Objects. This is done with a Managed Reference. It is very important that the only fields on one Managed Object that reference another Managed Object be Managed References. This is how the Data Manager knows that it is a reference to a separate Managed Object. If you store a simple Java reference to the second Managed Object in a field on the first Managed Object, the second object will become part of the first object’s state when the first object is stored. The result will be that, the next time the
first object tries to access the second, it will get its own local copy and not the real second Managed Object.
HelloPersistence3 below illustrates this by creating a second persistent object that is called from the TrivialTimedTask and that keeps the last-called time as part of its persistent state.
HelloPersistence3
HelloPersistence3, below, is a task that delegates to a sub-task (a TrivialTimedTask that is not scheduled to run on its own). The sub-task is stored in a Managed Reference on HelloPersistence3.
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson4;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedReference;
import com.sun.sgs.app.Task;
import com.sun.sgs.app.TaskManager;
/**
* A simple persistence example for the Project Darkstar Server.
*/
public class HelloPersistence3
implements AppListener, Serializable, Task
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloPersistence3.class.getName());
/** The delay before the first run of the task. */
public static final int DELAY_MS = 5000;
/** The time to wait before repeating the task. */
public static final int PERIOD_MS = 500;
/** A reference to our subtask, a {@link TrivialTimedTask}. */
private ManagedReference<TrivialTimedTask> subTaskRef = null;
/**
Project Darkstar Server Application Tutorial 12/19/08 29
* Gets the subtask this task delegates to. Dereferences a
* {@link ManagedReference} in this object that holds the subtask.
* <p>
* This null-check idiom is common when getting a ManagedReference.
*
* @return the subtask this task delegates to, or null if none is set
*/
public TrivialTimedTask getSubTask() {
if (subTaskRef == null)
return null;
return subTaskRef.get();
}
/**
* Sets the subtask this task delegates to. Stores the subtask
* as a {@link ManagedReference} in this object.
* <p>
* This null-check idiom is common when setting a ManagedReference,
* since {@link DataManager#createReference createReference} does
* not accept null parameters.
*
* @param subTask the subtask this task should delegate to,
* or null to clear the subtask
*/
public void setSubTask(TrivialTimedTask subTask) {
if (subTask == null) {
subTaskRef = null;
return;
}
DataManager dataManager = AppContext.getDataManager();
subTaskRef = dataManager.createReference(subTask);
}
// implement AppListener
/**
* {@inheritDoc}
* <p>
* Schedules the {@code run()} method of this object to be called
* periodically.
* <p>
* Since SGS tasks are persistent, the scheduling only needs to
* be done the first time the application is started. When the
* server is killed and restarted, the scheduled timer task will
* continue ticking.
* <p>
* Runs the task {@value #DELAY_MS} ms from now,
* repeating every {@value #PERIOD_MS} ms.
*/
public void initialize(Properties props) {
// Hold onto the task (as a managed reference)
setSubTask(new TrivialTimedTask());
TaskManager taskManager = AppContext.getTaskManager();
taskManager.schedulePeriodicTask(this, DELAY_MS, PERIOD_MS);
}
/**
* {@inheritDoc}
* <p>
* Prevents client logins by returning {@code null}.
*/
30 12/19/08 Project Darkstar Server Application Tutorial
public ClientSessionListener loggedIn(ClientSession session) {
return null;
}
// implement Task
/**
* {@inheritDoc}
* <p>
* Calls the run() method of the subtask set on this object.
*/
public void run() throws Exception {
// Get the subTask (from the ManagedReference that holds it)
TrivialTimedTask subTask = getSubTask();
if (subTask == null) {
logger.log(Level.WARNING, “subTask is null”);
return;
}
// Delegate to the subTask’s run() method
subTask.run();
}
}
Another way to register a Managed Object to the Data Manager is with the setBinding call. This call does not return a Managed Reference, but instead binds the Managed Object to the string passed in with it to the call.
Once a Managed Object has a name bound to it, the Managed Object may be retrieved by passing the same name to the getBinding call. Note that name bindings must be distinct. For each unique string used as a name binding by an application, there can be one and only one Managed Object bound.
Retrieving a Managed Object by its binding has some additional overhead, so it’s better to keep Managed References to Managed Objects in the other Managed Objects that need to call them. There are, however, some problems that are best solved with a name-binding convention; one common example is finding the player object for a particular player at the start of his or her session.
There are a number of other interesting methods on the Data Manager. You might want to look at the Javadoc now, but discussion of them will be put off until required by the tutorial applications.
Lesson 5: Hello User!
Up till now the tutorial lessons have focused on getting your logic up and running in the PDS. But there is another side to the online game equation ─ the users and their computers. This lesson shows how to start communicating between clients and the PDS.
In this tutorial, the server side of that communication will be explained and illustrated using a simple pre-built client. For the client-side coding, please see the Project Darkstar Client Tutorial.
Knowing When a User Logs In The first step in communicating with users is knowing who is available to communicate with. The PDS provides a callback method on the AppListener for this: loggedIn. The loggedIn method gets passed an object that describes the user; this object is called a ClientSession.11
Below is the code for HelloUser, a trivial application that logs the login of a user.
HelloUser
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson5;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
/**
* Simple example of listening for user {@linkplain AppListener#loggedIn login}
* in the Project Darkstar Server.
* <p>
* Logs each time a user logs in, then kicks them off immediately.
11 In fact, ClientSession describes the new connection session, the user being one of those parameters. This distinction is
important, in that you cannot save a ClientSession object and expect it to be valid after the session has ended, which is
when the user disconnects.
32 12/19/08 Project Darkstar Server Application Tutorial
*/
public class HelloUser
implements AppListener, // to get called during startup and login.
Serializable // since all AppListeners are ManagedObjects.
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloUser.class.getName());
// implement AppListener
/** {@inheritDoc} */
public void initialize(Properties props) {
// empty
}
/**
* {@inheritDoc}
* <p>
* Logs a message each time a new session tries to login, then
* kicks them out by returning {@code null}.
*/
public ClientSessionListener loggedIn(ClientSession session) {
// User has logged in
logger.log(Level.INFO, “User {0} almost logged in”, session.getName());
// Kick the user out immediately by returning a null listener
return null;
}
}
Direct Communication
You will note that, when you run the server application above and connect to it with a client, the client is immediately logged out. This is because we are returning null from loggedIn. The PDS interprets this as our rejecting the user. To accept the user and allow him or her to stay logged in, you need to return a valid ClientSessionListener. To be valid, this object must implement both ClientSessionListener and Serializable.
Below is HelloUser2, which does this.
HelloUser2
HelloUser2 is identical to HelloUser except for the loggedIn method:
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
Project Darkstar Server Application Tutorial 12/19/08 33
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson5;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
/**
* Simple example of listening for user {@linkplain AppListener#loggedIn login}
* in the Project Darkstar Server.
* <p>
* Logs each time a user logs in, and sets their listener to a
* new {@link HelloUserSessionListener}.
*/
public class HelloUser2
implements AppListener, // to get called during startup and login.
Serializable // since all AppListeners are ManagedObjects.
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloUser2.class.getName());
// implement AppListener
/** {@inheritDoc} */
public void initialize(Properties props) {
// empty
}
/**
* {@inheritDoc}
* <p>
* Logs a message each time a new session logs in.
*/
public ClientSessionListener loggedIn(ClientSession session) {
// User has logged in
logger.log(Level.INFO, “User {0} has logged in”, session.getName());
// Return a valid listener
return new HelloUserSessionListener(session);
}
}
HelloUserSessionListener
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
34 12/19/08 Project Darkstar Server Application Tutorial
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson5;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.ManagedReference;
/**
* Simple example {@link ClientSessionListener} for the Project Darkstar
* Server.
* <p>
* Logs each time a session receives data or logs out.
*/
class HelloUserSessionListener
implements Serializable, ClientSessionListener
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloUserSessionListener.class.getName());
/** The session this {@code ClientSessionListener} is listening to. */
private final ManagedReference<ClientSession> sessionRef;
/** The name of the {@code ClientSession} for this listener. */
private final String sessionName;
/**
* Creates a new {@code HelloUserSessionListener} for the given session.
*
* @param session the session this listener is associated with
*/
public HelloUserSessionListener(ClientSession session) {
if (session == null)
throw new NullPointerException(“null session”);
sessionRef = AppContext.getDataManager().createReference(session);
sessionName = session.getName();
}
Project Darkstar Server Application Tutorial 12/19/08 35
/**
* Returns the session for this listener.
*
* @return the session for this listener
*/
protected ClientSession getSession() {
// We created the ref with a non-null session, so no need to check it.
return sessionRef.get();
}
/**
* {@inheritDoc}
* <p>
* Logs when data arrives from the client.
*/
public void receivedMessage(ByteBuffer message) {
logger.log(Level.INFO, “Message from {0}”, sessionName);
}
/**
* {@inheritDoc}
* <p>
* Logs when the client disconnects.
*/
public void disconnected(boolean graceful) {
String grace = graceful ? “graceful” : “forced”;
logger.log(Level.INFO,
“User {0} has logged out {1}”,
new Object[] { sessionName, grace }
);
}
}
HelloUserSessionListener is a glue object that listens for either data from the user or the disconnect of the user;
it allows our server code to respond to these events. So far, all we do is log some information, but in a complete PDS application, these would both be important events to which we would want to respond.
There are two kinds of communication in the PDS:
● Direct Communication
● Channel Communication
Direct Communication is built into the core of the system and provides a pipe for the flow of data between a single user client and its PDS application.
Channel Communication is provided by a standard manager, the Channel Manager, and provides for publish/subscribe group communications. While there is nothing in the Channel Manager’s functionality that could not be implemented on top of the Direct Communication mechanisms, putting the channel functionality in a manager allows for a much more efficient implementation.
The HelloEcho PDS application echoes back to the user anything the user sends to the application. Besides the name, there is only one line difference in HelloEchoSessionListener from HelloUserSessionListener: the addition of a session.send call.
HelloEchoSessionListener
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
36 12/19/08 Project Darkstar Server Application Tutorial
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson5;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.ManagedReference;
/**
* Simple example {@link ClientSessionListener} for the Project Darkstar
* Server.
* <p>
* Logs each time a session receives data or logs out, and echoes
* any data received back to the sender.
*/
class HelloEchoSessionListener
implements Serializable, ClientSessionListener
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloEchoSessionListener.class.getName());
/** The session this {@code ClientSessionListener} is listening to. */
private final ManagedReference<ClientSession> sessionRef;
/** The name of the {@code ClientSession} for this listener. */
private final String sessionName;
/**
* Creates a new {@code HelloEchoSessionListener} for the given session.
*
* @param session the session this listener is associated with
*/
public HelloEchoSessionListener(ClientSession session) {
if (session == null)
throw new NullPointerException(“null session”);
sessionRef = AppContext.getDataManager().createReference(session);
sessionName = session.getName();
}
Project Darkstar Server Application Tutorial 12/19/08 37
/**
* Returns the session for this listener.
*
* @return the session for this listener
*/
protected ClientSession getSession() {
// We created the ref with a non-null session, so no need to check it.
return sessionRef.get();
}
/**
* {@inheritDoc}
* <p>
* Logs when data arrives from the client, and echoes the message back.
*/
public void receivedMessage(ByteBuffer message) {
ClientSession session = getSession();
logger.log(Level.INFO, “Message from {0}”, sessionName);
// Echo message back to sender
session.send(message);
}
/**
* {@inheritDoc}
* <p>
* Logs when the client disconnects.
*/
public void disconnected(boolean graceful) {
ClientSession session = getSession();
String grace = graceful ? “graceful” : “forced”;
logger.log(Level.INFO,
“User {0} has logged out {1}”,
new Object[] { sessionName, grace }
);
}
}
Running the Examples
To try all the examples in this part of the server tutorial, you need a simple client capable of logging in, as well as direct client/server communication. You can find this client as part of Lesson 1 of the Project Darkstar Client Tutorial (com.sun.sgs.tutorial.client.lesson1.HelloUserClient in the tutorial-client.jar file).
Lesson 6: Hello Channels!
The previous lessons have introduced the Task Manager and Data Manager. The final standard manager is the Channel Manager. The core of the PDS provides us with basic client/server communications. For simple games, this may be enough. However, for games that organize players into groups, either to isolate game sessions (such as in many casual and fast action games), or to tame the n-squared user-to-user communications scaling issues inherent in massive numbers of simultaneous players, something with lower overhead and more control is
required.
The Channel Manager provides publish/subscribe channels. The server application can create these channels and then assign users to one or more of them. Communication between users in a channel does not involve the Task or Data Manager.
Coding HelloChannels
The HelloChannels Managed Object is similar to our previous AppListener implementations with the addition
that it opens two reliable channels, Foo and Bar.
HelloChannels
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson6;
import java.io.Serializable;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.Channel;
import com.sun.sgs.app.ChannelManager;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.Delivery;
import com.sun.sgs.app.ManagedReference;
/**
* Simple example of channel operations in the Project Darkstar Server.
Project Darkstar Server Application Tutorial 12/19/08 39
* <p>
* Extends the {@code HelloEcho} example by joining clients to two
* channels.
*/
public class HelloChannels
implements Serializable, AppListener
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloChannels.class.getName());
/* The name of the first channel {@value #CHANNEL_1_NAME} */
static final String CHANNEL_1_NAME = “Foo”;
/* The name of the second channel {@value #CHANNEL_2_NAME} */
static final String CHANNEL_2_NAME = “Bar”;
/**
* The first {@link Channel}. The second channel is looked up
* by name.
*/
private ManagedReference<Channel> channel1 = null;
/**
* {@inheritDoc}
* <p>
* Creates the channels. Channels persist across server restarts,
* so they only need to be created here in {@code initialize}.
*/
public void initialize(Properties props) {
ChannelManager channelMgr = AppContext.getChannelManager();
// Create and keep a reference to the first channel.
Channel c1 = channelMgr.createChannel(CHANNEL_1_NAME,
null,
Delivery.RELIABLE);
channel1 = AppContext.getDataManager().createReference(c1);
// We don’t keep a reference to the second channel, to demonstrate
// looking it up by name when needed. Also, this channel uses a
// {@link ChannelListener} to filter messages.
channelMgr.createChannel(CHANNEL_2_NAME,
new HelloChannelsChannelListener(),
Delivery.RELIABLE);
}
/**
* {@inheritDoc}
* <p>
* Returns a {@link HelloChannelsSessionListener} for the
* logged-in session.
*/
public ClientSessionListener loggedIn(ClientSession session) {
logger.log(Level.INFO, “User {0} has logged in”, session.getName());
return new HelloChannelsSessionListener(session, channel1);
}
}
The HelloChannelsSessionListener is identical to HelloEchoSessionListener except for the constructor. When we create the session listener, we also join its session to two channels. One channel is passed in, while the second is looked up by name.
The first channel.join is passed null for a ChannelListener, so all communication on it is only received by clients. The second channel joined however is given a channel listener. This will be called back whenever a message from this session is posted to that channel. The listener can examine the message and sender and decide to discard the message, send a different message to the channel, or send the original, unmodified message to the channel.
Note that, with this code, each session has it own listener for messages on the second channel. This is preferable to registering a single channel-wide listener, since messages from different clients can be processed in parallel.
However, if your design really requires a single listener to all messages sent by any client on a channel, you would declare the listener as the second parameter to the createChannel call.
HelloChannelsSessionListener
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson6;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.AppContext;
import com.sun.sgs.app.Channel;
import com.sun.sgs.app.ChannelManager;
import com.sun.sgs.app.ClientSession;
import com.sun.sgs.app.ClientSessionListener;
import com.sun.sgs.app.DataManager;
import com.sun.sgs.app.ManagedReference;
/**
* Simple example {@link ClientSessionListener} for the Project Darkstar
* Server.
* <p>
* Logs each time a session receives data or logs out, and echoes
* any data received back to the sender.
*/
class HelloChannelsSessionListener
implements Serializable, ClientSessionListener
{
Project Darkstar Server Application Tutorial 12/19/08 41
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloChannelsSessionListener.class.getName());
/** The session this {@code ClientSessionListener} is listening to. */
private final ManagedReference<ClientSession> sessionRef;
/** The name of the {@code ClientSession} for this listener. */
private final String sessionName;
/**
* Creates a new {@code HelloChannelsSessionListener} for the session.
*
* @param session the session this listener is associated with
* @param channel1 a reference to a channel to join
*/
public HelloChannelsSessionListener(ClientSession session,
ManagedReference<Channel> channel1)
{
if (session == null)
throw new NullPointerException(“null session”);
DataManager dataMgr = AppContext.getDataManager();
sessionRef = dataMgr.createReference(session);
sessionName = session.getName();
// Join the session to all channels. We obtain the channel
// in two different ways, by reference and by name.
ChannelManager channelMgr = AppContext.getChannelManager();
// We were passed a reference to the first channel.
channel1.get().join(session);
// We look up the second channel by name.
Channel channel2 = channelMgr.getChannel(HelloChannels.CHANNEL_2_NAME);
channel2.join(session);
}
/**
* Returns the session for this listener.
*
* @return the session for this listener
*/
protected ClientSession getSession() {
// We created the ref with a non-null session, so no need to check it.
return sessionRef.get();
}
/**
* {@inheritDoc}
* <p>
* Logs when data arrives from the client, and echoes the message back.
*/
public void receivedMessage(ByteBuffer message) {
ClientSession session = getSession();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, “Message from {0}”, sessionName);
}
session.send(message);
42 12/19/08 Project Darkstar Server Application Tutorial
}
/**
* {@inheritDoc}
* <p>
* Logs when the client disconnects.
*/
public void disconnected(boolean graceful) {
String grace = graceful ? “graceful” : “forced”;
logger.log(Level.INFO,
“User {0} has logged out {1}”,
new Object[] { sessionName, grace }
);
}
}
HelloChannelsChannelListener is a simple skeletal listener that just logs what it receives.
HelloChannelsChannelListener
/*
* Copyright 2007-2008 Sun Microsystems, Inc.
*
* This file is part of Project Darkstar Server.
*
* Project Darkstar Server is free software: you can redistribute it
* and/or modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation and
* distributed hereunder to you.
*
* Project Darkstar Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sun.sgs.tutorial.server.lesson6;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.sgs.app.Channel;
import com.sun.sgs.app.ChannelListener;
import com.sun.sgs.app.ClientSession;
import java.nio.ByteBuffer;
/**
* Simple example {@link ChannelListener} for the Project Darkstar Server.
* <p>
* Logs when a channel receives data.
*/
class HelloChannelsChannelListener
implements Serializable, ChannelListener
{
/** The version of the serialized form of this class. */
private static final long serialVersionUID = 1L;
Project Darkstar Server Application Tutorial 12/19/08 43
/** The {@link Logger} for this class. */
private static final Logger logger =
Logger.getLogger(HelloChannelsChannelListener.class.getName());
/**
* {@inheritDoc}
* <p>
* Logs when data arrives on a channel. A typical listener would
* examine the message to decide whether it should be discarded,
* modified, or sent unchanged.
*/
public void receivedMessage(Channel channel,
ClientSession session,
ByteBuffer message)
{
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO,
“Channel message from {0} on channel {1}”,
new Object[] { session.getName(), channel.getName() }
);
}
channel.send(session, message);
}
}
Running HelloChannels
To try HelloChannels you need a client that will connect and allow you to talk on selected channels. One is provided in Lesson 2 of the Project Darkstar Client Tutorial
(com.sun.sgs.tutorial.client.lesson2.HelloChannelClient in tutorial-client.jar).
Conclusion
At this point you know all the basics of writing PDS applications. The applications you write using the PDS API on a single-node system will operate unmodified in exactly the same way on a large-scale multiple-node PDS production back end. In that environment they will scale out horizontally, handle failover, and be fault-tolerant.
There are, however, some best practices to follow to ensure optimal scalability for your application. Failing to follow these can seriously limit how many users your application will be able to support at once.
Best Practices
● Do not design with bottleneck Managed Objects.
You can think of each Managed Object as an independent worker who can only do one task at a time.
When one task is modifying the state of a Managed Object, any other tasks that want to read or change its state must wait. A Managed Object may have many readers at once if no one is writing to it, but any writing turns it into a potential bottleneck. In general, a pattern of one writer and many readers is the best configuration, although it is not always possible.
In the worst case, multiple lockers of the same objects will cause potential deadlock situations that are computationally more expensive to resolve and can result in processing delays (additional latency).
● Avoid contending for object access.
Although the PDS will detect and resolve contentions for write access to objects between parallel events such that your code never actually stops processing, this deadlock avoidance can significantly load the CPU and slow response time to the users. In the worst case, contention can actually prevent parallel processing, seriously impacting the scalability of your code.
A classic example of a deadlock is two combatants who have to modify both their own data and that of their enemies when resolving an attack. If all the data is on a combatant Managed Object, then both combat tasks need to lock both objects, typically in the opposite order, which is a formula for deadlock.
One solution is to divide the combatants’ combat statistics into two groups, the ones they modify themselves and the ones modified by an attacker, and store them on separate Managed Objects. The combatant object would then retain Managed References to these objects, and only lock them as needed.
Another practical solution is partial serialization of the problem. This is especially appropriate if you want the combatants attacks to be processed in a fixed order. Here you have a single “combat clock” task (generally a repeating task) that processes in sequence all the attacks from all the combatants in a given battle.
Not all contention situations are this easy to spot and design around. The PDS gives you feedback at runtime as to what objects are deadlocking. Use this to tune your application.
● Give all serializable objects a fixed serialVersionUID.
The PDS uses Serialization internally. If you don’t give your Serializable classes a serialVersionUID,
then any change to their public interface could invalidate the stored copies, leading to a need to delete the entire Object Store. Giving them a fixed serialVersionUID will allow you to make “compatible changes” without invalidating your existing store. (For what constitutes a compatible change, please see the JDK™ documents on Serialization.)
● Avoid inner classes in Serializable objects.
Serialization of objects whose classes contain non-static inner classes gets complicated. It is best to avoid inner classes, including anonymous inner classes. If you must use them, the safest thing is to declare them as static inner classes.
● Do not create non-final static fields on Managed Objects.
The most important reason for this is that static fields exist only within the scope of a single VM, and a PDS back end floats Managed Objects between many different VMs.
● Do not use the synchronized keyword in Managed Objects.
First, this is unnecessary in a PDS application. Synchronization is used to prevent contention over data between multiple parallel threads of control. The PDS programming model handles this transparently for you. Second, it won’t work, since synchronization is relative to a single VM and a full PDS back-end operates over many VM instances simultaneously. Finally, it causes interactions between tasks that can defeat the system’s deadlock-proofing feature and actually cause your code to lock up.
Any task that locks up for too long will be forced by the PDS to yield its control of Managed Objects back to the system, but this is a last-ditch safety feature and will result in significant delays in code execution.
● Do not make blocking I/O calls or stay in a loop for a long period.
The system contains the assumption that tasks are short-lived. If a task lives too long, it will be forcibly terminated by the back end. The right way to do blocking I/O and the like is to create an extension manager, do the blocking calls in it, and submit a task to the Task Manager to handle the results when done. (Writing and installing custom managers will be covered in the Project Darkstar Server Extension Manual.)
● Do not catch java.lang.Exception.
Instead, catch the explicit exceptions you are expecting. The PDS also uses exceptions to communicate exceptional states to the execution environment. Although the system does its best to do the right thing even if you hide these exceptions from it, it will operate more efficiently if you don’t.
● Do not carry a non-transient Java reference on a Managed Object to another Managed Object.
Instead, use a Managed Reference. Any object that is referred to by a Java reference chain that starts at the Managed Object is assumed to be part of the private state of that particular Managed Object. This means that, while you may set two Java references on two different Managed Objects to the same Java object during a task, they will each end up with their own copy of that object at the termination of the task.
● Never try to save a Manager Instance on a non-transient field of a Managed Object.
This is because Manager References are only valid for the life of the task that fetched them. Manager instances are not serializable objects. Any attempt to save a reference to one in your Managed Object will cause the Data Manager to throw a non-retriable exception and the entire task to be abandoned.
● Carefully manage the life cycle of your Managed Objects.
Remember that the Object Store does no garbage collection for you. Managed Objects are “real objects” in the simulation sense. They don’t exist until explicitly created, exist in one and only one state at any given time, and persist until explicitly destroyed. These are very good properties from a simulation programming stance, but if you go wild creating Managed Objects and don’t destroy them when they are no longer useful, you can load down the Object Store with garbage and potentially impact performance.
Be aware that any PDS API call that accepts a Managed Object may create a Managed Reference to that object. If this is the first time the Data Manager has been made aware of the Managed Object, this will result in the Managed Object being added to the Object Store. It is still the developer’s responsibility to remove the Managed Object from the Object Store when it is no longer in use. For this reason, it is best to avoid passing Managed Objects that your application is not explicitly managing into the PDS APIs.
Things Not Covered in This TutorialThis tutorial is intended to introduce you to the fundamentals of coding a PDS application. Although we present all basic uses of the standard managers, these managers have additional functions and capabilities not covered here. Please see the Javadoc for those other functions.
As discussed, the list of managers in a given PDS back end is extensible. This tutorial does not cover writing or using plug-in managers. For that, see the forthcoming document on PDS extensions.
Although we use PDS client programs in Lessons 5 and 6, this tutorial does not explain how those are written.
For those explanations, please see the Project Darkstar Client Tutorial.
Finally, for the sake of clarity, this tutorial shows very simple examples. For more complex patterns of PDS
usage, please see the community examples at www.projectdarkstar.com.
Appendix A: SwordWorld Example Code – 뺍니다. PDS 홈페이지에서 다운받으세요











 

이미 핑을 보냈습니다: