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)
- HelloWorld 코딩하기 (Coding HelloWorld)
- Lesson Two: Hello Logger!
- HelloLogger 코딩하기 (Coding HelloLogger)
- HelloLogger
- The Logging Properties File
- HelloLogger 코딩하기 (Coding HelloLogger)
- 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
- HelloPersistence 코딩하기 (Coding HelloPersistence)
-
- HelloPersistence3 코딩하기 (Coding HelloPersistence3)
-
- HelloPersistence3 26
- HelloPersistence3 코딩하기 (Coding HelloPersistence3)
- Lesson 5: Hello User!
- 사용자가 로그인할 때 일어나는 일 (Knowing When a User Logs In)
-
- HelloUser
- HelloUser
- 직접 통신 (Direct Communication)
-
- HelloUser2
- HelloUserSessionListener
- HelloEchoSessionListener
- HelloUser2
- 예제 실행하기 (Running the Examples)
- 사용자가 로그인할 때 일어나는 일 (Knowing When a User Logs In)
- Lesson 6: Hello Channels!
- HelloChannels 코딩하기 (Coding HelloChannels)
- HelloChannels
- HelloChannelsSessionListener
- HelloChannelsChannelListener
- HelloChannels
- HelloChannels 실행하기 (Running HelloChannels)
- HelloChannels 코딩하기 (Coding HelloChannels)
- 결론 (Conclusion)
- Best Practices
- 튜토리얼에 없는 내용 (Things Not Covered in This Tutorial)
- Best Practices
- Appendix A: SwordWorld Example Code
- Sword World
- SwordWorldObject
- SwordWorldRoom
- SwordWorldPlayer
- Sword World
소개 (Introduction)
PDS 어플리케이션 코딩하기 (Coding Project Darkstar Server Applications)
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.
태스크와 매니저 (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
- Data Manager
- 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.
클라이언트로부터 생성된 이벤트를 핸들링하는 태스크는 특정 규칙에 의해 실행이 보증됩니다. 한 클라이언트의 이전 이벤트가 모두 끝나기 전에는 그 이후 발생될 클라이언트 이벤트를 핸들링하는 태스크가 실행하지 않을 것입니다. 자식 태스크는 그 부모 태스크를 주목하고 있으나 형제 태스크에 관하여는 그렇지 않습니다. 이것은 자식 태스크의 실행이 부모 태스크의 실행이 끝나기 전 까지 시작되지 않는 것을 의미합니다. 그러나 형제 태스크에 관한 순서에 대한 보증은 존재하지 않습니다.
중요 : 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.
다른 태스크에 요청될 수 있는 자원들의 과도한 요청으로 블럭되지 않기 위해 태스크는 짧은 주기로 제공됩니다. 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 이라고 알려진 방법입니다.(연결-통과 스타일? -_-)
- 소비되는 시간의 계산이 종료되었을 때의 결과를 갖는 태스크를 큐에 넣어 처리하는 방법입니다.
- 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.
데이터 매니저는 오브젝트 스토어라고 하는 오프젝트의 풀에 저장된 매니지드 오브젝트의 퍼시스턴스 셋을 보존하고 있습니다. 보통 자바 오브젝트와 같이, 각 매니지드 오브젝트는 데이터와 데이터를 처리하기 위한 메소드를 포함합니다. 매니지드 오브젝트를 작성하려면 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는 매니지드 오브젝트의 상태가 변경될 것이라고 시스템에 통보합니다.
- get은 단지 상태를 read할 때 사용됩니다.
다른 매니지드 오브젝트의 모든 변경점이 변경되지 않는다 하여도(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.
일반적으로 매니지드 오브젝트는 세가지 타입입니다.
Managed Objects typically fall into three general types of entity:
- 칼, 몬스터, 활동공간(예를들면 방) 등과 같은 게임상의 가상환경에 동작하는 오브젝트.
- 근접 계산을 위한 quau-tree나 이동 경로 결정 work-mesh와 같은 로직 또는 데이터 구조.
- 게임 플레이어를 위한 매니지드 오브젝트 프록시
Figure 1 below illustrates a very basic world consisting of a single room that contains two players and a sword.
When deciding where to break data up into multiple Managed Objects, consider these questions:
- 얼마나 큰 데이터입니까? 자신의 상태에 한 개의 매니지드 오브젝트를 포함하는 큰 데이터는 읽고 쓰는데 더 많은 시간을 요합니다.
- 그 데이터의 결합도가 얼마나 가깝습니까? 일반적으로 공용으로 사용되는 데이터는 똑같은 매니지드 오브젝트에서 좀 더 효과적으로 저장됩니다. 독립적으로 사용되는 데이터는 다른 매니지드 오브젝트상으로 구분되기 위한 대상입니다. 핵심적으로 수정되어야 하는 데이터는 최우선적으로 같은 매니지드 오브젝트에 저장되어야 합니다.
- 특정 데이터를 동시에 접근하여야 하는 태스크들이 얼마나 많습니까? 위에 언급한 것에 따르면, PDS는 가능한한 병렬로 실행되는 많은 태스크와 같이 실행되기 위한 최선책을 수행합니다. 다중 병령 수행되는 태스크가 같은 매니지드 오브젝트의 상태를 변경할 때 충돌을 피하는 것은 많은 비용이 소요됩니다.
이러한 고찰을 통하여 볼 때 세번째 고려사항은 잘 동작하는 PDS 어플리케이션을 작성하기 위하여 가장 크리티컬한 주제입니다.
Of all these considerations, the third is the most critical to a well-running PDS application.
외부에서 이벤트가 발생할 때 매니저에 의한 이벤트 핸들러가 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 shows our simple Managed Object world, with two players connected to the PDS as clients.
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).
룸 매니지드 오브젝트의 매니지드 오브젝트를 구성하는 월드 상위에, 소드 매니지드 오브젝트와 한 상의 플레이어 매니지드 오브젝트가 있습니다. 그러나 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를 위치시킬 것입니다.
- 어플리케이션이 이전에 부팅된 적이 없다면, 이 어플리케이션의 오브젝트 스토어는 공백상태가 될 것이며 PDS는 AppListener의 동작에 실패할 것입니다. 이러한 이유로 어플리케이션은 매니지드 오브젝트인 AppListener를 생성해야 하며 초기화를 호출하는 태스크를 시작하여야 합니다.
- 반대로, 어플리케이션이 적어도 한번 이상 부팅된 적이 있다면, 오브젝트 스토어는 이미 메니지드 오브젝트인 AppListener는 오브젝트 스토어내에 포함되어 있을 것입니다. 따라서, 시스템이 다운되었을 때, 새로운 연결이 시도되었을 때, 이전에 동작했었던 어떤 주기적인 태스크를 동작시키고자 할 때의 실행은 그냥 재시작만 하면 됩니다.
다음 데모 어플리케이션의 의사코드에서 이와 같은 점을 볼 수 있습니다.
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 } |
In general, it is the responsibility of the AppListener to create the initial world of Managed Objects during the first startup.
그렇다면 사용자가 로그인 할 때 어떤 작업을 수행하여야 하겠습니까?
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);
} TO SAVED MANAGED OBJECT REF TO ROOM
GET ROOM MANAGED OBJECT } |
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.
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:
서버 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:
| 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. |
| 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. |
| 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. |
| 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!”);
} 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:
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의 표기법을 따릅니다. |
튜토리얼의 폴더를 보시면 컴파일된 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:
- For Windows:
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:
- PDS가 설치되어 있는 경로로 SGSHOME 환경변수를 추가합니다. Add the environment variable SGSHOME to your environment and set it to the directory where you installed the Project Darkstar Server.
- SGSHOME 환경변수를 path에 추가합니다. Add SGSHOME to your execution path, where SGSHOME is your PDS install directory.
- 쉘을 엽니다. Open a Unix shell, a Windows command window, or whatever you do to get a command line on your development system.
- 튜토리얼 디렉토리로 이동합니다. 유닉스 환경이라면 다음과 같이 명령을 낼 것입니다. Change your working directory to the tutorial directory of your PDS. In Unix, the command might be something like this:
- 다음을 타이핑 하십시오. Type the following:
- For Unix:
- For Windows:
- Add the environment variable SGSHOME to your environment and
set it to the directory where you installed the Project Darkstar Server. - Add SGSHOME to your execution path, where SGSHOME is your PDS install directory.
- Open a Unix shell, a Windows command window, or whatever you do to get a command line on your development system.
- Change your working directory to the tutorial directory of your PDS. In Unix, the command might be something like this:
- Type the following:
- For Unix:
- For Windows:
여러분은 “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 입니다.
|
PDS를 중지하고 HelloWorld 어플리케이션을 다시 동작시키려 한다면, “HelloWorld!” 출력이 나오질 않고 에러를 보게 될 것입니다. 이는 오브젝트 스토어에 이전에 동작하고 있던 AppListener가 이미 존재하므로 호출되지 않았던 초기화를 하여야 합니다.
HelloWorld를 다시 동작하고 싶다면, 여러분은 오브젝트 스토어의 내용을 지워야 합니다
- For Unix:
- For Windows:
(역자주 : 저도 지우려고 했는데 잘 지워지지 않더군요. 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:
- For Windows:
Lesson Two: Hello Logger!
HelloLogger 코딩하기 (Coding HelloLogger)
| 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
}
|
The Logging Properties File
● 태스크 매니저 Task Manager
● 데이터 매니저 Data Manager
● 채널 내니저 Channel Manager
● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)
● Task Manager
● Data Manager
● Channel Manager
● getTaskManager()
● getDataManager()
● getChannelManager()
● getManager(managerClass)
| 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
{
}
|
| 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
}
|
| 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 }
);
}
}
|
|
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();
}
}
|
|
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;
}
}
|
| 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 }
);
}
}
|
| 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 }
);
}
}
|
| 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);
}
}
|
| 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 |
|
/*
* 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);
}
}
|
이미 핑을 보냈습니다:
No related posts.
이 플러그인은 Yet Another Related Posts Plugin에 의해 개발되었습니다.
