Mina Framework Learning Notes (v)

Source: Internet
Author: User
Tags error handling

After describing the message formats in the example application, the specific "encoding" and "decoding" procedures are discussed below. The "encoding" process is accomplished by the encoder, which needs to implement the Org.apache.mina.filter.codec.ProtocolEncoder interface, generally inheriting from Org.apache.mina.filter.codec.ProtocolEncoderAdapter and overwrite the desired method. The implementation of the message encoder Commandencoder in the example application is given in Listing 6.
listing 6. Online Game example application of message encoder Commandencoder

public class Commandencoder extends Protocolencoderadapter {public 

    void encode (iosession session, Object message,< C3/>protocolencoderoutput out) throws Exception { 
        abstracttetriscommand command = (abstracttetriscommand) message; 
        byte[] bytes = Command.tobytes (); 
        Iobuffer buf = Iobuffer.allocate (Bytes.length, false); 
		
        Buf.setautoexpand (true); 
        Buf.putint (bytes.length); 
        Buf.put (bytes); 
		
        Buf.flip (); 
        Out.write (BUF); 
    } 

In Listing 6, the Encode method encapsulates the logic of the encoding. Because the tobytes of Abstracttetriscommand has already finished converting to byte array, the Encode method can be used directly. The encoding process is accomplished by first writing the length of the message body byte array, and then the byte array itself.

Compared with the coding process, the decoding process is relatively complex. The specific implementation is shown in Listing 7.
listing 7. Online Game example application of message decoder Commanddecoder

public class Commanddecoder extends Cumulativeprotocoldecoder {protected Boolean Dodecode (iosession session, Iobuff Er in, protocoldecoderoutput out) throws Exception {if (in.prefixeddataavailable (4, Constants.max_c 
            Ommand_length)) {int LENGTH = In.getint (); 
            byte[] bytes = new Byte[length]; 
            In.get (bytes); 
            int commandnamelength = Constants.command_name_length; 
            byte[] cmdnamebytes = new Byte[commandnamelength]; 
            System.arraycopy (bytes, 0, cmdnamebytes, 0, commandnamelength); 
            String cmdName = Stringutils.trim (new String (cmdnamebytes)); 
            Abstracttetriscommand command = tetriscommandfactory. Newcommand (CmdName); 
                if (command!= null) {byte[] cmdbodybytes = new Byte[length-commandnamelength]; System.arraycopy (Bytes, commandnamelength, cmdbodybytes, 0, Length-commandnamelength); 
                Command.bodyfrombytes (cmdbodybytes); 
            Out.write (command); 
        return true; 
        else {return false; } 
    } 
}

As you can see in Listing 7, the decoder Commanddecoder inherits from Cumulativeprotocoldecoder. This is a helper class provided by the Apache MINA that automatically caches all the data that has been received until the encoder thinks it can start coding. So when you implement your own encoder, you only need to consider how to determine the boundaries of the message. If the subsequent data for a message has not been received, Cumulativeprotocoldecoder is automatically cached. As mentioned before, an important issue in the decoding process is to judge the boundaries of a message. For fixed-length messages, it is only necessary to use the Iobuffer remaining method of Apache MINA to determine the number of bytes in the current cache, and to decode it if it is larger than the length of the message, and to indicate the length of the message body using a fixed-length message header, Iobuffer provides a prefixeddataavailable method to meet this requirement. Prefixeddataavailable checks whether the current cache has a fixed-length message header, and whether the message body that specifies the length of this message header is already in the cache. If both of these conditions are satisfied, a complete message has been received and can be decoded. The decoding process itself is not complex, first reading the category name of the message, and then using the Tetriscommandfactory.newcommand method to generate an instance of the message, and then through the instance's The Bodyfrombytes method can recover the contents of a message from a byte array and get a complete message object. Each time you successfully decode a message object, you need to call Protocoldecoderoutput write to pass the message object backwards. The message object will eventually reach the I/O processor through the filter chain and receive this message object in Iohandler.messagereceived. If the current cached data is not sufficient to decode a message, Dodecode only needs to return false. Once the new data is received, the Dodecode is called again.

Filter Chain

The

Filter works only when it is added to the filter chain. A filter chain is a container for a filter. The filter chain and the I/O session are one by one corresponding relationships. Org.apache.mina.core.filterchain.IoFilterChain is an interface to the filter chain in Apache Mina, which provides a series of methods for manipulating the filters contained therein, including querying, adding, deleting, and replacing. As shown in the   table 5 .
table 5. Methods for Iofilterchain interfaces

method
AddFirst (String Name, Iofilter filter) adds a filter of the specified name to the beginning of the filter chain. The
addlast (String name, Iofilter filter) adds a filter of the specified name to the end of the filter chain. The
contains (String name) determines whether the filter chain contains a filter with the specified name.
get (String name) Gets the filter of the specified name from the filter chain. The
Remove (String name) deletes the filter of the specified name from the filter chain.
replace (String name, Iofilter newfilter) replaces the filter  newfilter with a filter chain named  na Filter for me. The
getsession () Gets the I/O session corresponding to the filter chain one by one.

After introducing the I/O filter and the filter chain, the I/O processor is described below.




Back to the top of the page


I/O processor

I/O events reach the I/O processor after the filter chain. The method corresponding to I/O events in the I/O processor is invoked. Apache MINA Org.apache.mina.core.service.IoHandler is an interface to be implemented by I/O processors and, in general, simply inherit from Org.apache.mina.core.service.IoHandlerAdapter and overwrite the desired method. The Iohandler interface method is shown in table 6.
Table 6. The method of Iohandler interface

method
sessioncreated (I Osession Session This method is invoked when a new connection is established.
sessionopened (iosession session) This method is invoked when a new connection is opened. The method is invoked after  sessioncreated.
sessionclosed (iosession session) This method is invoked when the connection is closed.
Sessionidle (iosession session, idlestatus status) This method is invoked when the connection becomes idle.
Exceptioncaught (iosession session, Throwable Cause) when I/O processor implementation or Apache MINA This method is invoked when an exception is thrown in the
messagereceived (iosession session, Object message) This method is invoked when a new message is received.
Messagesent (iosession session, Object message) This method is invoked when a message is successfully sent out.

For the methods in table 6, there are several explanations that need to be highlighted. The first is the difference between sessioncreated and sessionopened. The Sessioncreated method is invoked by the I/O processing thread, and the sessionopened is invoked by other threads. Therefore, for performance reasons, do not perform too many operations in the Sessioncreated method. For Sessionidle, the idle time setting is disabled by default, meaning that Sessionidle is not invoked. Can be set by Iosessionconfig.setidletime (Idlestatus, int).

The basic concepts in the Apache MINA have been introduced, and the use of state machines is described below.




Back to the top of the page


Using the state machine

When implementing business logic in an I/O processor, it is generally only necessary to handle incoming messages in the Messagereceived method for simple situations. If you need to write back data to the peer, use the Iosession.write method. In other cases, the client and server side of the communication protocol is more complex, the client is actually a state change. This can be achieved with the state machine provided in Apache MINA, making it easier to implement the I/O processor.

Two important elements in a state machine are the state and the migration between States. The state of the client in the example application and the migration are shown in Figure 5.
Figure 5. The status of the client in the online game example application and the migration

Client initialization, its status is "not connected", indicating that the client has not registered on the server, at this time can not play; then the user needs to enter a nickname to register to the server, after completion of the state migration to "idle." The client will now receive a list of all other users currently online. The current user can invite other users to play a game with him, or they can receive invitations from other users. After the invitation is sent out, the client's status is migrated to invite sent. If you accept an invitation from another user, the client's status is migrated to invite received. If a user's invitation is accepted by another user, the status of the two clients will be migrated to the game.

To implement such a more complex state machine, it is only necessary to define state and migration conditions in a declarative manner in the I/O processor. First you need to declare state in the state machine, as shown in Listing 8.
Listing 8. Status declaration in application of online game example

@State public static final String root = "root"; 
@State (ROOT) public static final String not_connected = "notconnected"; 
@State (ROOT) public static final String IDLE = "IDLE"; 
@State (ROOT) public static final String invitation_sent = "Invitationsent"; 
@State (ROOT) public static final String invitation_accepted = "invitationaccepted"; 
@State (ROOT) public static final String PLAYING = "PLAYING"; 

As shown in Listing 8, a total of six states are defined above. A state is declared by a callout @State. It should be noted that the state can be inherited between. If the state machine receives an event, the corresponding migration is not found in the current state, and the lookup continues on its parent state. State inheritance can be useful in some situations, such as the desire to add the same migration logic to all States, so that the migration condition is added directly to the parent state. A typical scenario is error handling, in general, all States require error handling, and the logic of error handling is generally the same. You can describe this scenario succinctly by placing the migration in the parent state in the event of an error.

After a state is defined, the migration between States should be declared below. As shown in Listing 9.
Listing 9. The status migration statement in the application of the online game example

@IoHandlerTransition (on = message_received, in = not_connected, next = IDLE) public 
void Login (Tetrisservercontext Co ntext, iosession session, Logincommand cmd) { 
    String nickname = Cmd.getnickname (); 
    Context.nickname = nickname; 
    Session.setattribute ("nickname", nickname); 
    Session.setattribute ("status", Userstatus.idle); 
    Sessions.add (session); 
    Users.add (nickname); 
		
    Refreshplayerslistcommand command = Createrefreshplayerslistcommand (); 
    Broadcast (command); 
  
@IoHandlerTransition (on = exception_caught, in = ROOT, weight = ten) public 
void Exceptioncaught (iosession session, Ex Ception e) { 
    Logger.warn ("Unexpected error.", e); 
    Session.close (true); 

@IoHandlerTransition (in = ROOT, weight = m) public 
void Unhandledevent () { 
    Logger.warn ("unhandled event."); 
} 

In Listing 9, a State migration is declared using the callout @IoHandlerTransition. Each state migration can have four properties: on, in, next, and weight, where property in is mandatory and the rest is optional. The attribute on indicates the name of the event that triggered this state migration, and if omitted, the default is the wildcard character that matches all events. The value of this property can be seven types of events that can be handled in the I/O processor given in the table. Property in represents the starting state of the State migration. The property next represents the end state of the State migration, which, if omitted, defaults to the _self_ that represents the current state. Attribute weight is used to indicate the weight of the State migration. All migrations of a state are sorted in ascending order of their weights. For the current state, if there are multiple possible migrations, the sorted forward migration will occur. The first annotation in the code declares that if the current state is "disconnected" and receives the Message_received event, and the content of the message is a Logincommand object, the login method is invoked and the current state is migrated to "idle" after the call completes. The second annotation declares that for any state, the Exceptioncaught method is invoked if the Exception_caught event is received. The last annotation declares a state migration whose starting state is ROOT, indicating that the migration works for all events. But its weight is 100, the priority is relatively low. The role of this state migration is to handle other events that do not have a corresponding state migration.

After using the state machine provided by Apache MINA, the way in which I/O processors are created has changed. An instance of the I/O processor is created by the state machine, as shown in Listing 10.
listing 10. Creating an I/O processor in the state machine

private static Iohandler Createiohandler () { 
    statemachine sm = statemachinefactory.getinstance ( 
        Iohandlertransition.class). Create (serverhandler.not_connected, 
        new Serverhandler ()); 
    return new Statemachineproxybuilder (). Setstatecontextlookup (New 
        Iosessionstatecontextlookup Statecontextfactory () {public 
            Statecontext create () {return 
                new Serverhandler.tetrisservercontext (); 
            } 
    )). Create (Iohandler.class, SM); 
}

In Listing 10, Tetrisservercontext is a context object that is provided to the state machine to share data between states. Of course it can be done with iosession, but the benefit of the context object is type safety and no additional type conversions are required.

After introducing the state machine, here are some advanced topics, including asynchronous operations and the integration of Apache MINA with JMX and Spring.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.