diff --git a/binaries/DotNetMQ.exe b/binaries/DotNetMQ.exe new file mode 100644 index 0000000..db366af Binary files /dev/null and b/binaries/DotNetMQ.exe differ diff --git a/binaries/DotNetMQ.exe.config b/binaries/DotNetMQ.exe.config new file mode 100644 index 0000000..c5fd44f --- /dev/null +++ b/binaries/DotNetMQ.exe.config @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/binaries/INSTALL_x64.bat b/binaries/INSTALL_x64.bat new file mode 100644 index 0000000..83074e1 --- /dev/null +++ b/binaries/INSTALL_x64.bat @@ -0,0 +1,2 @@ +C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil DotNetMQ.exe +net start DotNetMQ diff --git a/binaries/INSTALL_x86.bat b/binaries/INSTALL_x86.bat new file mode 100644 index 0000000..35eca6f --- /dev/null +++ b/binaries/INSTALL_x86.bat @@ -0,0 +1,2 @@ +C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil DotNetMQ.exe +net start DotNetMQ diff --git a/binaries/MDSCommonLib.XML b/binaries/MDSCommonLib.XML new file mode 100644 index 0000000..1a3512a --- /dev/null +++ b/binaries/MDSCommonLib.XML @@ -0,0 +1,3601 @@ + + + + MDSCommonLib + + + + + This message is used to acknowledge/reject a message and to send a MDSDataTransferMessage in same message object. + It is used in web services. + + + + + Abstract class of all message classes. + All messages transmiting on MDS must be derrived from this class. + + + + + This interface is implemented by all classes that can be serialized/deserialized by MDS Serialization. + + + + + This method serializes the object. + + Used to serialize object + + + + This method deserializes the object. + + Used to deserialize object + + + + Constructor. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + Unique ID for this message. + Thiss will be a GUID if it is not set. + + + + + If this message is a reply for another message then RepliedMessageId contains first message's MessageId + else RepliedMessageId is null default. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + This field is used to acknowledge/reject to an incoming message. + + + + + This field is used to send a new message. + + + + + This message is sent from MDS manager to MDS server to update server graph of MDS. + + + + + This is the base class for all messages that are being transmited between MDS server and Management (Controller) application. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeIf of message. + It is used to serialize/deserialize message. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for UpdateServerGraphMessage. + + + + + The ServerGraphInfo object that stores all server and graph informations. + + + + + This class is used by MDS Web Services to serialize/deserialize/create messages. + + + + + Deserializes an incoming message for Web Service from MDS server. + + Message as byte array + Deserialized message + + + + Serializes a message to send to MDS server from Web Service. + + Message to serialize + Serialized message + + + + Checks a response message whether it is a valid response message + + Message to check + + + + Implements IWebServiceIncomingMessage to be used by MDS web service. + + + + + Represents a Data Transfer message. + Used to transfer real message data between client applications. + + + + + Creates a new MDSDataTransferMessage object. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + Name of the first source server of the message. + + + + + Name of the first source application of the message. + If the message is created by an MDS (source of message is not an application) + then SourceApplicationName must be set to null. + + + + + The source communication channel's (Communicator's) Id. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicates a spesific communicator. + This field is set by MDS automatically. + + + + + Name of the final destination server of the message. + + + + + Name of the final destination application of the message. + If the message is sent to an MDS (destination of message is not an application), + then DestinationApplicationName must be set to null. + + + + + Destination communication channel's (Communicator's) Id. + This field is used by MDS to deliver message to a spesific communicator. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicate a spesific communicator as receiver of message. + If it is set to 0 (zero), message may be delivered to any connected communicator. + If there is no communicator with DestinationCommunicatorId, message can not be delivered, so, + this field can only be used to send non-persistent messages. + + + + + Passed servers of message until now, includes source and destination servers. + + + + + Essential application message data to transfer. + + + + + Transmit rule of message. + This is important because it determines persistence and transmit time of message. + Default: StoreAndForward. + + + + + Represents an incoming data message to a MDS Web Service from MDS server. + + + + + Gets the unique identifier for this message. + + + + + Name of the first source server of the message. + + + + + Name of the sender application of the message. + + + + + The source communication channel's (Communicator's) unique Id. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicate a spesific communicator. + This field is set by MDS automatically. + + + + + Name of the final destination server of the message. + + + + + Name of the final destination application of the message. + + + + + Passed servers of message until now, includes source and destination servers. + + + + + Essential application message data that is received. + + + + + Transmit rule of message. + This is important because it determines persistence and transmit time of message. + Default: StoreAndForward. + + + + + Creates a new IncomingDataMessage object from a MDSDataTransferMessage object. + + MDSDataTransferMessage object to create IncomingDataMessage + + + + Creates IWebServiceResponseMessage using this incoming message, to return from web service to MDS server. + + Response message to this message + + + + Creates IWebServiceOutgoingMessage using this incoming message, to return from web service to MDS server. + + Response message to this message + + + + Implements IWebServiceOutgoingMessage to be used by MDS web service. + + + + + Represents an outgoing data message from a MDS web service to MDS server. + + + + + Essential application message data to be sent. + + + + + Implements IWebServiceResponseMessage to be used by MDS web service. + + + + + Represents a response message to a IWebServiceIncomingMessage from a MDS web service. + + + + + Process result of IWebServiceIncomingMessage. + + + + + Response message to IWebServiceIncomingMessage. + This may be null to do not send a response to incoming message. + + + + + Implements IWebServiceOperationResultMessage to be used by MDS web service. + + + + + This message is sent to clients as a response to an operation. + It is generally used to send an ACK/Reject message for a message + or a response to register message. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + Operation result. + True, if operation is successful. + + + + + A text that may be used as a description for result of operation. + + + + + Represents a result message for an incoming message to MDS Web Service. + + + + + Operation result. + True, if operation is successful. + + + + + A text that may be used as a description for result of operation. + + + + + Any MDSService class must be inherited from this class. + + + + + This method generates client proxy class to use this service. + It is also a MDSServiceMethod, so, clients can update it's proxy classes via calling this method remotely. + + Namespace of generating proxy class + Proxy class code to use this service + + + + This method can be used to check if service is available. + + A string message + Reply to message as formatted: "RE:message". + + + + Normalizes some known primitive types. + + Type name + Normalized type name + + + + Checks if a method is predefined method (MDSService methods in MDSService class). + + Method name to check + True: Yes, it is.. + + + + When a method of a MDSService application is invoked, this field stores address of source application in MDS. + + + + + When a method of a MDSService application is invoked, this field stores the original message that is sent by MDS server. + + + + + This message is sent as return message of a MDSRemoteInvokeMessage. + It is used to send return value of method invocation. + It is sent by MDSServiceApplication class and received by MDSServiceProxyBase class. + + + + + Return value of remote method invocation. + + + + + If any exception occured during method invocation, this field contains Exception object. + If no exception occured, this field is null. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetApplicationWebServicesMessage. + + + + + Name of the application to get web service information. + + + + + Web service communicators of application. + + + + + Represents a thread-safe string-key based object collection. + + + + + All objects are stored in this collection. + + + + + Contructor. + + + + + Removes an object from collection. + + Key of object to remove + + + + Rewmoves all keys/values from collection. + + + + + Gets the synchronization object that is used by + + + + + Gets/sets an object with given key. + + Key + Object with given key, or null if key does not exists + + + + This interface is used to serialize primitives and objects. + Only needed Write methods designed for MDS. + + + + + Serializes a byte. + + byte to serialize + + + + Writes a byte array to serialization stream. + Byte array may be null or empty. + + byte array to write + + + + Serializes an integer. + + integer to serialize + + + + Serializes an unsigned integer. + + unsigned integer to serialize + + + + Serializes a long. + + long to serialize + + + + Serializes a boolean. + + boolean to serialize + + + + Serializes a DateTime object. + + DateTime to serialize + + + + Serializes a char according to UTF8. + Char may be null or empty. + + char to serialize + + + + Serializes a string according to UTF8. + String may be null or empty. + + string to serialize + + + + Serializes an object that implements IMDSSerializable interface. + Object may be null. + + object to serialize + + + + Serializes an array that all items implements IMDSSerializable interface. + Object array may be null or empty. + + objects to serialize + + + + This class is used to store transmit informations of a message throught a server. + + + + + Creates a new ServerTransmitReport. + + + + + Serializes this object. + + Serializer used to serialize objects + + + + Deserializes this object. + + Deserializer used to deserialize objects + + + + Name of the server. + + + + + Message arriving time to server. + + + + + Message leaving time from server. + + + + + A delegate to create events when a data transfer message received from MDS server. + + The object which raises event + Event arguments + + + + Stores message informations. + + + + + Received message from MDS server. + + + + + Any MDSService class must has this attribute. + + + + + Creates a new MDSServiceAttribute object. + + + + + A brief description of Service. + + + + + Service Version. This property can be used to indicate the code version (especially the version of service methods). + This value is sent to user application on an exception, so, user/client application can know that service version is changed. + Default value: NO_VERSION. + + + + + Represents an Timeout exception. + + + + + Represents a MDS Exception. + This is the base class for exceptions that are thrown by MDS system. + + + + + Contstructor. + + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + This interface is used to Write/Read messages according to a Wire/Communication Protocol. + + + + + Serializes and writes a MDSMessage according to the protocol rules. + + Serializer to serialize message + Message to be serialized + + + + Reads and constructs a MDSMessage according to the protocol rules. + + Deserializer to read message + MDSMessage object that is read + + + + This message is used to get web services informations of an application from MDS server. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetApplicationWebServicesMessage. + + + + + Name of the application to get web service information. + + + + + This message is sent to all connected MDS managers/controllers by MDS Server to inform when a client application is removed. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for ClientApplicationRemovedEventMessage. + + + + + Name of the new application. + + + + + This class stores some consts used in MDS. + + + + + Default IP address to connect to MDS server. + + + + + Default listening port of MDS server. + + + + + Maximum message data length. + + + + + Respesents states of an incoming message. + + + + + Message is waiting for Ack. + + + + + Message is acknowledged. + + + + + Message is rejected. + + + + + This is the base class for proxy classes that is used to make remote invocation to a MDSService. + + + + + Reference to the underlying MDSServiceConsumer object to send/receive MDS messages. + + + + + Name of the service class. + + + + + Initializes MDSServiceProxyBase. + + Reference to a MDSServiceConsumer object to send/receive MDS messages + Address of remote application + Name of the service class + + + + Sends remote method invocation message to the remote application and gets result. + This simplifies remove method invocation like calling a method locally. + It throws Exception if any Exception occured on remote application's method. + + Method name to invoke + Method parameters + Return value of remote method + + + + Sends remote method invocation message to the remote application and gets result. + This simplifies remove method invocation like calling a method locally. + It throws Exception if any Exception occured on remote application's method. + + Method name to invoke + Method parameters + + + + MDS Address of remote application. + + + + + Transmit rule of underlying messages. + Using this peoperty, connection between applications can be changes from tight coupled to loose coupled and vice versa. + Just methods whose return type is void, can use other transmit rule than DirectlySend. So, that methods may be loose coupled by setting transmit rule. + Methods that has return value always use DirectlySend transmit rule, even if it is set by user to another rule. So, that methods must be tight coupled. + Default value: DirectlySend. + + + + + Timeout for service method calls as milliseconds. + Default: 300000 (5 minutes). + + + + + This class is used as base class for classes that are processing messages of an application concurrently. Thus, + an application can process more than one message in a time. + MDS creates an instance of this class for every incoming message to process it. + Maximum limit of messages that are being processed at the same time is configurable for individual applications. + + + + + Base class for MDSMessageProcessor/MDSClientApplicationBase. + + + + + Reference to the MDS Server. + + + + + Reference to this Application. + + + + + This method is called by MDS server to process the message, when a message is sent to this application. + + Message to process + + + + Used to get/set if messages are auto acknowledged. + If AutoAcknowledgeMessages is true, then messages are automatically acknowledged after MessageReceived event, + if they are not acknowledged/rejected before by application. + Default: true. + + + + + A threaded queue that process only one item in a time and keeps others in a queue. + + Type of the processing item + + + + This interface is used for a class that can startable and stoppable. + + + + + This method is used to start running. + + + + + This method is used to stop running. + + Indicates that caller thread waits stopping of module + + + + Joins module's thread until it stops. + + + + + Queue object to store items. + + + + + Running thread. + + + + + Thread control flag. + + + + + Construnctor. + + + + + Starts the processing of items. Thread runs, listens and process items on queue. + + + + + Stops the processing of items and stops the thread. + + True, if caller method must wait until running stops. + + + + Waits stopping of thread, thus waits end of execution of currently processing item. + + + + + Adds given item to queue to process. + + + + + + Thread's running method. Listens queue and processes items. + + + + + This method is used to raise ProcessItem event. + + The item that must be processed + Waiting item count on queue except this one + + + + This event is used to process get and process an item from queue. When an item inserted this + queue, ProcessItem event is raised. + + + + + This exception is thrown when there is not a communicator of a remote application. + + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + A delegate to create events when a control message received from MDS server. + + The object which raises event + Event arguments + + + + Stores message informations. + + + + + Creates a ControlMessageReceivedEventArgs object. + + Received message from MDS server + + + + Received message from MDS server. + + + + + Represents states of a communication object. + + + + + Connecting now.. + + + + + Connection is established, communication can be made. + + + + + Closing connection.. + + + + + Connection is closed, so communication can not be made. + + + + + A delegate to create events for changing state of a communication channel. + Old state is passed with event arguments, new state can be get from communication channel object (sender). + + The communication channel object which raises event + Event arguments + + + + Stores informations about communication channel's state. + + + + + The state of the client before change. + + + + + Represents a MDS Application from MDSMessageProcessor perspective. + This class also provides a static collection that can be used as a cache, + thus, an MDSMessageProcessor/MDSClientApplicationBase can store/get application-wide objects. + + + + + Name of the application. + + + + + Gets/Sets application-wide object by a string key. + + Key of the object to access it + Object, that is related with given key + + + + This message is sent from MDS manager to MDS server to get list of all client applications. + + + + + Gets MessageTypeId for GetApplicationListMessage. + + + + + A delegate to create events by Communication Channels, when a MDSMessage received from MDS server. + + The object which raises event + Event arguments + + + + Stores message informations. + + + + + Received message from MDS server. + + + + + This interface is used to deserialize primitives and objects. + Only needed Read methods designed for MDS. + + + + + Deserializes and returns a serialized byte. + + Deserialized byte + + + + Reads a byte array from deserializing stream. + Created byte array may be null or empty. + + Deserialized string + + + + Deserializes and returns a serialized integer. + + Deserialized integer + + + + Deserializes and returns a serialized unsigned integer. + + Deserialized unsigned integer + + + + Deserializes and returns a serialized long. + + Deserialized long + + + + Deserializes and returns a serialized boolean. + + Deserialized boolean + + + + Deserializes and returns a serialized DateTime object. + + Deserialized DateTime object + + + + Deserializes and returns a serialized char using UTF8. + + Deserialized char + + + + Deserializes and returns a serialized string using UTF8. + Created string may be null or empty. + + Deserialized string + + + + Deserializes and returns an object that implements IMDSSerializable. + Object creation method is passed as parameter and used to create empty object. + Created object may be null. + + A class that implements IMDSSerializable + A function that creates an empty T object + Deserialized object + + + + Deserializes and returns an array of objects that implements IMDSSerializable. + Object creation method is passed as parameter and used to create empty object. + Created array may be null or empty. + + A class that implements IMDSSerializable + A function that creates an empty T object + Deserialized object + + + + This message is used to get an operation result message. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for OperationResultMessage. + + + + + True, if operation is successfully executed. + + + + + If Success = True then "Success.", else error message. + + + + + Represents a web service communicator information for an application. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Url of the web service. + + + + + This message is sent by MDS Manager to MDS Server to add a new Application to MDS. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for AddNewApplicationMessage. + + + + + Name of the new application. + + + + + Plug-In applications may derive this class to perform some operations on starting/stopping of MDS. + + + + + This method is called on startup of MDS. + + + + + This method is called on stopping of MDS. + + + + + This class can be used to set/get settings to/from a registry key with caching capability. + + + + + Reference to logger. + + + + + Cached settings. + + + + + Creates a new RegistrySettings instance. + + + + + Gets an integer value from registry. It gets from cache if it is cached before and Caching enables. + + Value Name on the registry key + Default value, if no value exists on registry + Value for registryName + + + + Sets an integer value to registry. + + Value Name on the registry key + New value to set + + + + Gets an string value from registry. It gets from cache if it is cached before and Caching enables. + + Value Name on the registry key + Default value, if no value exists on registry + Value for registryName + + + + Sets a string value to registry. + + Value Name on the registry key + New value to set + + + + Sets a value to registry. + + Value Name on the registry key + New value to set + + + + Creates a registry key. + + Registry key to create + Created registry key + + + + Tries open a registry key and return it as writable/readable. + If key doesn't exists then creates and returns it. + + Registry key + Registry key + + + + Gets a value from registry. + + Name in registry key + Default value that is returned if name can not be found in registry key + Value of name entry in registry key + + + + Gets a value from registry. + + Name in registry key + Default value that is returned if name can not be found in registry key + If this is true and registryKey is not exists, then it is created + Value of name entry in registry key + + + + Sets a value on registry. + + Name in registry key + value to set + + + + Registry key to store settings of application. + + + + + Indicates that if RegistrySettings uses caching. + + + + + A delegate to used by QueueProcessorThread to raise processing event + + Type of the item to process + The object which raises event + Event arguments + + + + Stores processing item and some informations about queue. + + + + + + Constructor. + + The item to process + The item count waiting for processing on queue (after this one) + + + + The item to process. + + + + + The item count waiting for processing on queue (after this one). + + + + + Register Message. A MDSRegisterMessage object is used to register a MDS server as an Application or MDS server. + + + + + Creates a new MDSRegisterMessage object. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + Communicator type (MDS server, Application or Controller). + + + + + Communication way for this communicator (SEND, RECEIVE or BOTH) + + + + + Name of the communicator. + If CommunicatorType is a MDS, than this is server's name, + if CommunicatorType is an Application, than this is application's name, + if CommunicatorType is a Controller, than this is an arbitrary string represents controller. + + + + + Password to connect to MDS associated with Name and CommunicatorType. + + + + + This message is sent by MDS Manager to MDS Server to remove a Application from MDS. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for RemoveApplicationMessage. + + + + + Name of the removing application. + + + + + This message is sent from MDS server to MDS manager as a response to GetApplicationListMessage message. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetApplicationListResponseMessage. + + + + + List of client applications. + + + + + This class is used to transfer simple information about a MDS Client Application. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Name of the client application + + + + + Currently connected (online) communicator count. + + + + + All Communication channels implements this interface. + It is used by MDSClient and MDSController classes to communicate with MDS server. + + + + + Connects to MDS server. + + + + + Disconnects from MDS server. + + + + + Sends a MDSMessage to the MDS server + + Message to be sent + + + + This event is raised when the state of the communication channel changes. + + + + + This event is raised when a MDSMessage object is received from MDS server. + + + + + Unique identifier for this communicator in connected MDS server. + This field is not set by communication channel, + it is set by another classes (MDSClient) that are using + communication channel. + + + + + Gets the state of communication channel. + + + + + Communication way for this channel. + This field is not set by communication channel, + it is set by another classes (MDSClient) that are using + communication channel. + + + + + This is the main class that is used by an client application to communicate with MDS server. + + + + + Reference to logger. + + + + + Communication channel that is used to communicate with MDS server. + + + + + This messages are waiting for a response. + Key: MessageID of waiting request message. + Value: A WaitingMessage instance. + + + + + This queue is used to queue and process sequentially messages that are received from MDS server. + + + + + This timer is used to reconnect to MDS server if it is disconnected. + + + + + Used to Start/Stop MDSClient, and indicates the state. + + + + + Creates a new MDSClient object using default IP address (127.0.0.1) and default TCP port. + + Name of the client application that connects to MDS server + + + + Creates a new MDSClient object using default TCP port. + + Name of the client application that connects to MDS server + Ip address of the MDS server + + + + Creates a new MDSClient object. + + Name of the client application that connects to MDS server + Ip address of the MDS server + Listening TCP Port of MDS server + + + + Connects to MDS server. + If ReConnectServerOnError is true, than does not throw any Exception. + Else, throws Exception if can not connect to the Server. + + + + + Disconnects from MDS server. + + + + + Creates an empty message to send. + + Created message + + + + Disposes this object. + + + + + Connects and registers to MDS server. + + + + + Changes communication way of this application. + So, a receiver may want to do not receive messages anymore by changing it's communication way to 'Send'. + + New communication way + + + + Sends a mssage to MDS server and waits a response for timeoutMilliseconds milliseconds. + + Message to send + What type of response is being waited + Maximum waiting time for response + Received message from server + + + + Sends a MDSMessage object to MDS server. + + + + + + This method handles StateChanged event of communication channel. + + The communication channel + Event arguments + + + + This event handles incoming messages from communication channel. + + Communication channel that received message + Event arguments + + + + This event handles processing messages when a message is added to queue (_incomingMessageQueue). + + Reference to message queue + Event arguments + + + + This method is called by _reconnectTimer_Tick to reconnect MDS server if disconnected. + + This argument is not used + + + + Sends a Ping message to MDS server if 60 seconds passed after last communication. + + + + + Closes communication channel, thus disconnects from MDS server if it is connected. + + + + + Checks if client application is connected to MDS server. + + True, if connected. + + + + This event is raised when a data transfer message received from MDS server. + + + + + Name of the client application. + + + + + Gets/Sets Communication Way between MDS server. + A receiver may want to do not receive messages anymore, by changing it's communication way to 'Send'. + Setting this property sends a MDSChangeCommunicationWayMessage message to MDS server. + Default value: CommunicationWays.SendAndReceive + + + + + Communicator Id of this instance of application in MDS. + This field is valid only if client application is connected and registered to the MDS server. + + + + + Gets/sets Reconnecting option on any error case. + If this is true, client application attempts to reconnect to MDS server until it is connected and + doesn't throw exceptions while connecting. + Default value: True. + + + + + Used to get/set if messages are auto acknowledged. + If AutoAcknowledgeMessages is true, then messages are automatically acknowledged after MessageReceived event, + if they are not acknowledged/rejected before by application. + Default: false. + + + + + Timeout value for outgoing data messages. + Default value: 300000 (5 minutes). + + + + + Time of last message received from MDS server. + + + + + Time of last message sent to MDS server. + + + + + MessageId of last received and acknowledged message's Id. + This field is used to do not receive/accept same message again. + If a message is send by MDS server with same MessageId, + message is discarded and ACK message is sent to server. + + + + + Implements IIncomingMessage to send Ack/Reject via MDSClient. + + + + + Represents an incoming data message for a MDS client. + + + + + Used to Acknowledge this message. + Confirms that the message is received safely by client application. + A message must be acknowledged by client application to remove message from message queue. + MDS server doesn't send next message to the client application until the message is acknowledged. + Also, MDS server sends same message again if the message is not acknowledged in a certain time. + + + + + Used to reject (to not acknowledge) this message. + Indicates that the message can not received correctly or can not handled the message, and the message + must be sent to client application later again. + If MDS server gets reject for a message, + it doesn't send any message to the client application instance for a while. + If message is persistent, than it is sent to another instance of application or to same application instance again. + If message is not persistent, it is deleted. + + + + + Used to reject (to not acknowledge) this message. + Indicates that the message can not received correctly or can not handled the message, and the message + must be sent to client application later again. + If MDS server gets reject for a message, + it doesn't send any message to the client application instance for a while. + If message is persistent, than it is sent to another instance of application or to same application instance again. + If message is not persistent, it is deleted. + + Reject reason + + + + Creates a empty response message for this message. + + Response message object + + + + Acknowledgment state of the message. + + + + + Gets the unique identifier for this message. + + + + + If this message is a reply for another message then RepliedMessageId contains first message's MessageId + else RepliedMessageId is null as default. + + + + + Name of the first source server of the message. + + + + + Name of the sender application of the message. + + + + + The source communication channel's (Communicator's) unique Id. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicate a spesific communicator. + This field is set by MDS automatically. + + + + + Name of the final destination server of the message. + + + + + Name of the final destination application of the message. + + + + + Destination communication channel's (Communicator's) Id. + This field is used by MDS to deliver message to a spesific communicator. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicate a spesific communicator as receiver of message. + If it is set to 0 (zero), message may be delivered to any connected communicator. + + + + + Passed servers of message until now, includes source and destination servers. + + + + + Essential application message data that is received. + + + + + Transmit rule of message. + This is important because it determines persistence and transmit time of message. + Default: StoreAndForward. + + + + + Reference to the MDSClient object. + + + + + Creates a new IncomingDataMessage object from a MDSDataTransferMessage object. + + Reference to the MDSClient object + MDSDataTransferMessage object to create IncomingDataMessage + + + + Used to Acknowledge this message. + Confirms that the message is received safely by client application. + A message must be acknowledged by client application to remove message from message queue. + MDS server doesn't send next message to the client application until the message is acknowledged. + Also, MDS server sends same message again if the message is not acknowledged in a certain time. + + + + + Used to reject (to not acknowledge) this message. + Indicates that the message can not received correctly or can not handled the message, and the message + must be sent to client application later again. + If MDS server gets reject for a message, + it doesn't send any message to the client application instance for a while. + If message is persistent, than it is sent to another instance of application or to same application instance again. + If message is not persistent, it is deleted. + + + + + Used to reject (to not acknowledge) this message. + Indicates that the message can not received correctly or can not handled the message, and the message + must be sent to client application later again. + If MDS server gets reject for a message, + it doesn't send any message to the client application instance for a while. + If message is persistent, than it is sent to another instance of application or to same application instance again. + If message is not persistent, it is deleted. + + Reject reason + + + + Creates a empty response message for this message. + + Response message object + + + + Acknowledgment state of the message. + + + + + Implements IOutgoingMessage to send message via MDSClient. + + + + + Represents an outgoing data message for a MDS client. + + + + + Sends the message to the MDS server. + If this method does not throw an exception, + message is correctly delivered to MDS server (persistent message) + or to the destination application (non persistent message). + + + + + Sends the message to the MDS server. + If this method does not throw an exception, + message is correctly delivered to MDS server (persistent message) + or to the destination application (non persistent message). + + Timeout to send message as milliseconds + + + + Sends the message and waits for an incoming message for that message. + MDS can be used for Request/Response type messaging with this method. + Default timeout value: 5 minutes. + + Response message + + + + Sends the message and waits for an incoming message for that message. + MDS can be used for Request/Response type messaging with this method. + + Timeout to get response message + Response message + + + + Unique ID for this message. + This will be a GUID if it is not set. + It is recommended to leave this field as default. + + + + + If this message is a reply for another message then RepliedMessageId contains first message's MessageId + else RepliedMessageId is null default. + + + + + Name of the first source server of the message. + If this field is leaved null/empty, it is set by MDS server automatically. + + + + + Name of the first sender application of the message. + If this field is leaved null/empty, it is set by MDS server as sender application's name automatically. + + + + + Name of the final destination server of the message. + If this field is leaved null/empty, it is set by MDS server as + sender application's server's name automatically. + It may be leaved null to send a message to an application on the same server. + + + + + Name of the final destination application of the message. + If this field is leaved null/empty, it is set by MDS server as sender application's name automatically. + It may be leaved null to send a message to same application. + + + + + Destination communication channel's (Communicator's) Id. + This field is used by MDS to deliver message to a spesific communicator. + When more than one communicator of an application is connected same MDS server + at the same time, this field may be used to indicates a spesific communicator as receiver of message. + If it is set to 0 (zero), message may be delivered to any connected communicator. + If there is no communicator with DestinationCommunicatorId, message can not be delivered, so, + this field can only be used to send non-persistent messages (Syncronous messages). + + + + + Essential application message data to be sent. + + + + + Transmit rule of message. + This is important because it determines persistence and transmit time of message. + Default: StoreAndForward. + + + + + Reference to the MDSClient object. + + + + + Creates a new OutgoingDataMessage object. + + Reference to the MDSClient object + + + + Sends the message to the MDS server. + If this method does not throw an exception, + message is correctly delivered to MDS server (persistent message) + or to the destination application (non persistent message). + + + + + Sends the message to the MDS server. + If this method does not throw an exception, + message is correctly delivered to MDS server (persistent message) + or to the destination application (non persistent message). + + Timeout to send message as milliseconds + + + + Sends the message and waits for an incoming message for that message. + MDS can be used for Request/Response type messaging with this method. + Default timeout value: 5 minutes. + + Response message + + + + Sends the message and waits for an incoming message for that message. + MDS can be used for Request/Response type messaging with this method. + + Timeout to get response message as milliseconds + Response message + + + + This class is used as item in _waitingMessages collection. + Key: Message ID to wait response. + Value: ManualResetEvent to wait thread until response received. + + + + + Creates a new WaitingMessage. + + + + + What type of message is being waited. + For MDSOperationResultMessage, it is MDSMessageFactory.MessageTypeIdMDSOperationResultMessage. + For MDSDataTransferMessage, it is MDSMessageFactory.MessageTypeIdMDSDataTransferMessage. + + + + + Response message received for sent message + This message may be MDSOperationResultMessage + or MDSDataTransferMessage according to WaitingResponseType. + + + + + ManualResetEvent to wait thread until response received. + + + + + State of the message. + + + + + This message is response to MDS Manager for GetApplicationWebServicesMessage. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetApplicationWebServicesMessage. + + + + + True, if operation is success and no error occured. + + + + + Detailed information about operation result. Error text, if any error occured. + + + + + Web service communicators of application. + + + + + Communication ways. + A client application may just send messages from communication channel or it can send and receive messages. + + + + + Application can only send messages to MDS server. + + + + + Application can send and receive messages to/from MDS server. + + + + + This class is the default deserializer of MDS. + The deserializing object must be serialized by MDSDefaultSerializer. + Only needed deserializers designed for MDS. + + + + + The stream that is used to read serialized items for deserializing. + + + + + Creates a new MDSDefaultDeserializer object. + + The stream that is used to read serialized items for deserializing + + + + Deserializes and returns a serialized byte. + + Deserialized byte + + + + Reads a byte array from deserializing stream. + Created byte array may be null or empty. + + Deserialized string + + + + Deserializes and returns a serialized integer. + + Deserialized integer + + + + Deserializes and returns a serialized unsigned integer. + + Deserialized unsigned integer + + + + Deserializes and returns a serialized long. + + Deserialized long + + + + Deserializes and returns a serialized boolean. + + Deserialized boolean + + + + Deserializes and returns a serialized DateTime object. + + Deserialized DateTime object + + + + Deserializes and returns a serialized char using UTF8. + Note: A better way may be found. + + Deserialized char + + + + Deserializes and returns a serialized string using UTF8. + Created string may be null or empty. + + Deserialized string + + + + Deserializes and returns an object that implements IMDSSerializable. + Object creation method is passed as parameter and used to create empty object. + Created object may be null. + + A class that implements IMDSSerializable + A function that creates an empty T object + Deserialized object + + + + Deserializes and returns an array of objects that implements IMDSSerializable. + Object creation method is passed as parameter and used to create empty object. + Created array may be null or empty. + + A class that implements IMDSSerializable + A function that creates an empty T object + Deserialized object + + + + Reads a byte array with spesified length. + + Length of the byte array to read + Read byte array + + + + Throws an exception for + + + + + This class is used to connect to and communicate with MDS server from MDS Manager (Controller). + + + + + Reference to logger. + + + + + Communication channel that is used to communicate with MDS server. + + + + + This queue is used to queue MDSMessage objects received from MDS server and process them sequentially. + + + + + This collection is used to send message and get response in SendMessageAndGetResponse method. + SendMessageAndGetResponse method must wait until response received. It waits using this collection. + Key: Message ID to wait response. + Value: ManualResetEvent to wait thread until response received. + + + + + This timer is used to reconnect to MDS server if it is disconnected. + + + + + Used to Start/Stop MDSController, and indicates the state. + + + + + Creates a new MDSClient object. + + Ip address of the MDS server + Listening TCP Port of MDS server + + + + Connects to MDS server. + + + + + Disconnects from MDS server. + + + + + Disposes MDSController object. + It also disconnects from server if it is connected. + + + + + Sends a ControlMessage to MDS server. + + Message to send + + + + Sends a ControlMessage to MDS server and gets it's response message. + + Message to send + Response message from server + + + + Connects and registers to MDS server. + + + + + Sends a MDSMessage object to MDS server. + + + + + + This event handles incoming messages from communication channel. + + Communication channel that received message + Event arguments + + + + This event handles processing messages when a message is added to queue (_incomingMessageQueue). + + Reference to message queue + Event arguments + + + + This method is called by _reconnectTimer_Tick to reconnect MDS server if disconnected. + + This argument is not used + + + + Sends a Ping message to MDS server if 60 seconds passed after last communication. + + + + + Closes communication channel, thus disconnects from MDS server if it is connected. + + + + + Checks if client application is connected to MDS server. + + True, if connected. + + + + Deserializes a ControlMessage from a MDSControllerMessage. + + MDSControllerMessage that includes ControlMessage + Deserialized ControlMessage object. + + + + This event is raised when a data transfer message received from MDS server. + + + + + Gets sets Reconnecting option on any error case. + If this is true, controller application attempts to reconnec to MDS server until it is connected, + MDSController doesn't throw exceptions while connecting. + Default value: True. + + + + + Time of last message received from MDS server. + + + + + Time of last message sent to MDS server. + + + + + + Creates a new WaitingMessage. + + + + + ManualResetEvent to wait thread until response received. + + + + + Response message received for sent message + + + + + This message is sent from MDS server to MDS manager as a response to GetServerGraphMessage message. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetServerGraphMessage. + + + + + The ServerGraphInfo object that stores all server and graph informations. + + + + + This class is used to simplify serialization/deserialization with MDS serialization classes. + + + + + Serializes an object that implements IMDSSerializable and returns serialized byte array. + + Object to serialize + Serialized object as byte array + + + + Serializes an object that implements IMDSSerializable to a Stream. + + Stream to write serialized object + Object to serialize + + + + Deserializes an object from a byte array. + + Type of object. This type must implement IMDSSerializable interface + A function that creates an instance of that object (T) + Byte array + Deserialized object + + + + Deserializes an object via reading from a stream. + + Type of object. This type must implement IMDSSerializable interface + A function that creates an instance of that object (T) + Deserialized object + Deserialized object + + + + This class is used to perform common tasks that is used in both client and server side. + + + + + This code is used to connect to a TCP socket with timeout option. + + IP endpoint of remote server + Timeout to wait until connect + Socket object connected to server + + + + Gets the current directory of executing assembly. + + Directory path + + + + Represents a MDS Remote Exception. + This exception is used to send an exception from an application to another application. + + + + + Contstructor. + + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + This message is used to change Communication Way of a communicator while it is connected to the MDS server. + Thus, for example, a receiver may change it's communication way to only Send and it does not get messages + anymore but can send messages. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + New communication way. + + + + + This message is sent by MDS Server to MDS Manager as a response to a RemoveApplicationMessage. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for RemoveApplicationResponseMessage. + + + + + Name of the new application. + + + + + True, if application is successfully removed. + + + + + If Removed = True then "Success", else error message. + + + + + This message is sent from MDS manager to MDS server to get graph of MDS servers. + + + + + Gets MessageTypeId for GetServerGraphMessage. + + + + + This class is used to connect to MDS server via TCP sockets. + + + + + Reference to logger. + + + + + IP address of MDS server + + + + + TCP port of MDS server + + + + + The TCP socket to the remote application. + + + + + The main stream wraps socket to send/receive data. + + + + + This object is used to send/receive messages as byte array. + + + + + The thread that listens incoming data. + + + + + Used to send only one message in a time by locking. + + + + + Creates a new TCPChannel object. + + IP address of MDS server + TCP port of MDS server + + + + Connects to MDS server. + + + + + Disconnects from MDS server. + + + + + Sends a MDSMessage to the MDS server + + Message to send + + + + Entrance point of the thread. + This method run by thread to listen incoming data from communicator. + + + + + Sends MDSMessage object to the socket. + + Message to be sent + + + + Changes the state of the client and raises StateChanged event. + + New state + + + + When a MDSMessage received from MDS server, this method is called to raise MessageReceived event. + + Incoming message from server + + + + This event is raised when the state of the client changes. + + + + + This event is raised when a MDSMessage received. + + + + + Unique identifier for this communicator in connected MDS server. + This field is not set by communication channel, + it is set by another classes (MDSClient) that are using + communication channel. + + + + + Gets the connection state of communicator. + + + + + Communication way for this channel. + This field is not set by communication channel, + it is set by another classes (MDSClient) that are using + communication channel. + + + + + Creates a new MDSServiceApplication object with default values to connect to MDS server. + + Name of the application + + + + Creates a new MDSServiceApplication object with default port to connect to MDS server. + + Name of the application + IP address of MDS server + + + + Creates a new MDSServiceApplication object. + + Name of the application + IP address of MDS server + TCP port of MDS server + + + + This method connects to MDS server using underlying MDSClient object. + + + + + This method disconnects from MDS server using underlying MDSClient object. + + + + + Disposes this object, disposes/closes underlying MDSClient object. + + + + + Initializes this object. + + + + + Underlying MDSClient object to send/receive MDS messages. + + + + + This class is the default serializer of MDS. + The serialized object must be deserialized by MDSDefaultDeserializer. + Only needed serializers designed for MDS. + + + + + The stream that is used to write serialized items. + + + + + Creates a new MDSDefaultSerializer object. + + The stream that is used to write serialized items + + + + Serializes a byte. + + byte to serialize + + + + Writes a byte array to serialization stream. + Byte array may be null or empty. + + byte array to write + + + + Serializes a integer. + + integer to serialize + + + + Serializes an unsigned integer. + + unsigned integer to serialize + + + + Serializes a long. + + long to serialize + + + + Serializes a boolean. + + boolean to serialize + + + + Serializes a DateTime object. + + DateTime to serialize + + + + Serializes a char according to UTF8. + Char may be null or empty. + Note: A better way may be found. + + char to serialize + + + + Serializes a string according to UTF8. + String may be null or empty. + + string to serialize + + + + Serializes an object that implements IMDSSerializable interface. + Object may be null. + + object to serialize + + + + Serializes an array that all items implements IMDSSerializable interface. + Object array may be null or empty. + + objects to serialize + + + + This class is used to send Ping messages to check if remote application is connected and working. + MDS Servers send Ping messages to other MDS servers and gets response. + Client applications send Ping messages to MDS servers and gets response. + If there is no Ping message from a remote application for a while, connection is closed and + reconnected if needed. + + + + + MessageTypeId of message. + It is used to serialize/deserialize message. + + + + + This class is created to make easy common GUI tasks. + + + + + Show a message box that show an error. + + Message to show + + + + Show a message box that show an error. + + Message to show + Caption of message box + + + + Show a message box that show an warning. + + Message to show + + + + Show a message box that show an warning. + + Message to show + Caption of message box + + + + Shows a messagebox to ask a question to user. + + Message to show + Caption of message box + User's choice + + + + Shows a messagebox to ask a question to user. + + Message to show + Caption of message box + Default selected button + User's choice + + + + Shows a messagebox that shows an information. + + Message to show + Caption of message box + + + + This interface is used by MDSMessageProcessor/MDSClientApplicationBase to perform operations on MDSServer, + for example; creating messages to send. + + + + + Creates an empty message to send. + + Created message + + + + Message transmit rules. + All messages are persistent except 'DirectlySend'. + If a server doesn't stores message and transmiting it directly, + it transmits this message before than a stored (persistent) message. + + + + + Not persistent message. + Message may be lost in an error. + Message is not stored on any server. + Message is not guarantied to be delivered. + This rule may be used if both of source and destination applications must be run at the same time. + If no exception received while sending message, + that means message delivered to and acknowledged by destination application correctly. + This rule blocks sender application until destination application sends ACK for message. + + + + + Persistent Message. + Message can not be lost and it is being stored in all passing servers. + Message is guarantied to be delivered and it will be delivered as ordered (FIFO). + This is the slowest but most reliable rule. + This rule blocks sender application until source (first) MDS server stores message. + + + + + Non-persistent message. + Message will be lost if MDS server which has message shuts down. + Message is not guarantied to be delivered. + + + + + This class represents a message that is being transmitted between MDS server and a Controller (MDS Manager). + + + + + MessageTypeId for MDSControllerMessage. + + + + + MessageTypeId of ControllerMessage. + This field is used to deserialize MessageData. + All types defined in ControlMessageFactory class. + + + + + Essential message data. + This is a serialized object of a class in MDS.Communication.Messages.ControllerMessages namespace. + + + + + Stores all MDS server's and server graph's informations. + + + + + Serializes this object. + + Serializer used to serialize objects + + + + Deserializes this object. + + Deserializer used to deserialize objects + + + + Reference to this server on graph (This server is also in Servers list). + + + + + All servers on graph. + + + + + Serializes this object. + + Serializer used to serialize objects + + + + Deserializes this object. + + Deserializer used to deserialize objects + + + + Name of this server. + + + + + IP address of this server. + + + + + TCP Port number that is listened by this server. + + + + + List of adjacent servers of this server that are splitted by , or ; + + + + + Location of server (Left (X) and Top (Y) properties in design area, seperated by comma (,)). + + + + + This message is sent to all connected MDS managers/controllers by MDS Server to inform about latest informations/state of a client application. + + + + + Serializes this message. + + Serializer used to serialize objects + + + + Deserializes this message. + + Deserializer used to deserialize objects + + + + Gets MessageTypeId for GetApplicationListResponseMessage. + + + + + Name of the client application + + + + + Currently connected (online) communicator count. + + + + + This delegate is used with IMDSDeserializer to deserialize an object. + It is used by IMDSDeserializer to create an instance of deserializing object. + So, user of MDS serialization must supply a method that creates an empty T object. + This is needed for performance reasons. Because it is slower to create object by reflection. + + Type of the object to be deserialized + An object from type T + + + + Represents types of communicatiors. + + + + + An undefined remote application. + + + + + A MDS server. + + + + + A client application. + + + + + A controller application. + + + + + This attribute is used to add information to a parameter or return value of a MDSServiceMethod. + + + + + Creates a new MDSServiceMethodParameterAttribute. + + + + + + A brief description of parameter. + + + + + This class is the Default Protocol that is used by MDS to communicate with other applications. + A message frame is sent and received by MDSDefaultWireProtocol: + + - Protocol type: 4 bytes unsigned integer. + Must be MDSDefaultProtocolType for MDSDefaultWireProtocol. + - Message type: 4 bytes integer. + Must be defined in MDSMessageFactory class. + - Serialized bytes of a MDSMessage object. + + + + + Specific number that a message must start with. + + + + + Serializes and writes a MDSMessage according to the protocol rules. + + Serializer to serialize message + Message to be serialized + + + + Reads and constructs a MDSMessage according to the protocol rules. + + Deserializer to read message + MDSMessage object that is read + + + + Represents a Database exception. + + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + Executed query text + + + + + This class ensures to use a class that is derived from MDSService class, as a service on MDS. + + + + + Underlying MDSClient object to send/receive MDS messages. + + + + + Reference to logger. + + + + + The service object that handles all method invokes. + + + + + Creates a new MDSServiceApplication object with default values to connect to MDS server. + + Name of the application + + + + Creates a new MDSServiceApplication object with default port to connect to MDS server. + + Name of the application + IP address of MDS server + + + + Creates a new MDSServiceApplication object. + + Name of the application + IP address of MDS server + TCP port of MDS server + + + + This method connects to MDS server using underlying MDSClient object. + + + + + This method disconnects from MDS server using underlying MDSClient object. + + + + + Adds a new MDSService for this service application. + + Service to add + + + + Removes a MDSService from this service application. + + Service to add + + + + Disposes this object, disposes/closes underlying MDSClient object. + + + + + Initializes this object. + + + + + This method handles all incoming messages from MDS server. + + Creator object of method (MDSClient object) + Message event arguments + + + + Sends an Exception to the remote application that invoked a service method + + Incoming invoke message from remote application + Exception to send + + + + Sends return value to the remote application that invoked a service method. + + Incoming invoke message from remote application + Return value to send + + + + Acknowledges a message. + + Message to acknowledge + + + + Represents a MDSService object. + + + + + Name of the Service object's class. + + + + + This collection stores a list of all methods of T, if that can be invoked from remote applications or not. + Key: Method name + Value: True, if it can be invoked from remote application. + + + + + Creates a new ServiceObject. + + The service object that is used to invoke methods on + MDSService attribute of service object's class + + + + Invokes a method of Service object. + + Name of the method to invoke + Parameters of method + Return value of method + + + + Rejects a message. + + Message to reject + Reject reason + + + + The service object that is used to invoke methods on. + + + + + MDSService attribute of Service object's class. + + + + + Represents an Serialization / Deserialization exception. + + + + + Contstructor. + + + + + Contstructor. + + Exception message + + + + Contstructor. + + Exception message + Inner exception + + + + Any MDSService class must use this attribute on it's remote methods. + If a method has not MDSServiceMethod attribute, it can not be invoked by remote applications. + + + + + A brief description (and may be usage) of method. + + + + + This message is sent to invoke a method of an application that implements MDSService API. + It is sent by MDSServiceProxyBase class and received by MDSServiceApplication class. + + + + + Name of the target service class. + + + + + Method of remote application to invoke. + + + + + Parameters of method. + + + + diff --git a/binaries/MDSCommonLib.dll b/binaries/MDSCommonLib.dll new file mode 100644 index 0000000..dac1048 Binary files /dev/null and b/binaries/MDSCommonLib.dll differ diff --git a/binaries/MDSCore.XmlSerializers.dll b/binaries/MDSCore.XmlSerializers.dll new file mode 100644 index 0000000..ce838ed Binary files /dev/null and b/binaries/MDSCore.XmlSerializers.dll differ diff --git a/binaries/MDSCore.dll b/binaries/MDSCore.dll new file mode 100644 index 0000000..e67093f Binary files /dev/null and b/binaries/MDSCore.dll differ diff --git a/binaries/MDSManager.exe b/binaries/MDSManager.exe new file mode 100644 index 0000000..6821ed2 Binary files /dev/null and b/binaries/MDSManager.exe differ diff --git a/binaries/MDSManager.exe.config b/binaries/MDSManager.exe.config new file mode 100644 index 0000000..ff3cef3 --- /dev/null +++ b/binaries/MDSManager.exe.config @@ -0,0 +1,36 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/binaries/MDSServiceProxyGenerator.exe b/binaries/MDSServiceProxyGenerator.exe new file mode 100644 index 0000000..91792a8 Binary files /dev/null and b/binaries/MDSServiceProxyGenerator.exe differ diff --git a/binaries/MDSSettings.design.xml b/binaries/MDSSettings.design.xml new file mode 100644 index 0000000..0f978c4 --- /dev/null +++ b/binaries/MDSSettings.design.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/binaries/MDSSettings.xml b/binaries/MDSSettings.xml new file mode 100644 index 0000000..9b73fda --- /dev/null +++ b/binaries/MDSSettings.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/binaries/MySql.Data.dll b/binaries/MySql.Data.dll new file mode 100644 index 0000000..7aa95ec Binary files /dev/null and b/binaries/MySql.Data.dll differ diff --git a/binaries/Setup/Databases/MSSQL/DB-FILES.zip b/binaries/Setup/Databases/MSSQL/DB-FILES.zip new file mode 100644 index 0000000..a40dafa Binary files /dev/null and b/binaries/Setup/Databases/MSSQL/DB-FILES.zip differ diff --git a/binaries/Setup/Databases/MSSQL/ReadMe.txt b/binaries/Setup/Databases/MSSQL/ReadMe.txt new file mode 100644 index 0000000..3d26e28 --- /dev/null +++ b/binaries/Setup/Databases/MSSQL/ReadMe.txt @@ -0,0 +1,6 @@ +To create MS SQL Server database for DotNetMQ, fallow one of two option below: + + First option : Create a database in SQL Server named mds and run query in Tables.txt file. + Second option : Unzip DB-FILES.zip and attach to SQL Server. + +After creating database, you must configure MDSSettings.xml to use MS SQL Server as DB engine in DotNetMQ. \ No newline at end of file diff --git a/binaries/Setup/Databases/MSSQL/Tables.txt b/binaries/Setup/Databases/MSSQL/Tables.txt new file mode 100644 index 0000000..1e72ccd --- /dev/null +++ b/binaries/Setup/Databases/MSSQL/Tables.txt @@ -0,0 +1,36 @@ +USE [mds] +GO + +/****** Object: Table [dbo].[Messages] Script Date: 05/22/2011 22:18:28 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +SET ANSI_PADDING ON +GO + +CREATE TABLE [dbo].[Messages]( + [Id] [int] IDENTITY(1,1) NOT NULL, + [MessageId] [nvarchar](100) NOT NULL, + [DestServer] [nvarchar](100) NOT NULL, + [NextServer] [nvarchar](100) NOT NULL, + [DestApplication] [nvarchar](100) NOT NULL, + [MessageData] [varbinary](max) NOT NULL, + [MessageDataLength] [int] NOT NULL, + [RecordDate] [datetime] NOT NULL, + CONSTRAINT [PK_messages] PRIMARY KEY CLUSTERED +( + [Id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +GO + +SET ANSI_PADDING OFF +GO + +ALTER TABLE [dbo].[Messages] ADD CONSTRAINT [DF_messages_RecordDate] DEFAULT (getdate()) FOR [RecordDate] +GO + diff --git a/binaries/Setup/Databases/MySQL/ReadMe.txt b/binaries/Setup/Databases/MySQL/ReadMe.txt new file mode 100644 index 0000000..49028f8 --- /dev/null +++ b/binaries/Setup/Databases/MySQL/ReadMe.txt @@ -0,0 +1,2 @@ +To create MySQL database for DotNetMQ, create a database in MySQL Server named 'mds' and run query in Tables.txt file. +After creating database, you must configure MDSSettings.xml to use MySQL Server as DB engine in DotNetMQ. \ No newline at end of file diff --git a/binaries/Setup/Databases/MySQL/Tables.txt b/binaries/Setup/Databases/MySQL/Tables.txt new file mode 100644 index 0000000..d366dab --- /dev/null +++ b/binaries/Setup/Databases/MySQL/Tables.txt @@ -0,0 +1,13 @@ +CREATE TABLE `mds`.`messages` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `MessageId` varchar(100) NOT NULL, + `DestServer` varchar(100) NOT NULL, + `NextServer` varchar(100) NOT NULL, + `DestApplication` varchar(100) NOT NULL, + `MessageData` blob NOT NULL, + `MessageDataLength` int(10) unsigned NOT NULL, + `RecordDate` datetime NOT NULL, + PRIMARY KEY (`Id`), + KEY `IX_Ser_App` (`NextServer`,`DestApplication`,`Id`) USING BTREE, + KEY `IX_Ser` (`NextServer`,`Id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=latin5; \ No newline at end of file diff --git a/binaries/SqliteDB/MDS.s3db b/binaries/SqliteDB/MDS.s3db new file mode 100644 index 0000000..1a1cdce Binary files /dev/null and b/binaries/SqliteDB/MDS.s3db differ diff --git a/binaries/SqliteDB/ReadMe.txt b/binaries/SqliteDB/ReadMe.txt new file mode 100644 index 0000000..b8a9cbe --- /dev/null +++ b/binaries/SqliteDB/ReadMe.txt @@ -0,0 +1 @@ +MDS.s3db is an empty SQLite database file to be used in DotNetMQ. \ No newline at end of file diff --git a/binaries/System.Data.SQLite.dll b/binaries/System.Data.SQLite.dll new file mode 100644 index 0000000..aa398bb Binary files /dev/null and b/binaries/System.Data.SQLite.dll differ diff --git a/binaries/UNINSTALL_x64.bat b/binaries/UNINSTALL_x64.bat new file mode 100644 index 0000000..aa4f722 --- /dev/null +++ b/binaries/UNINSTALL_x64.bat @@ -0,0 +1,2 @@ +net stop DotNetMQ +C:\Windows\Microsoft.NET\Framework64\v4.0.30319\installutil -u DotNetMQ.exe diff --git a/binaries/UNINSTALL_x86.bat b/binaries/UNINSTALL_x86.bat new file mode 100644 index 0000000..8f2eff7 --- /dev/null +++ b/binaries/UNINSTALL_x86.bat @@ -0,0 +1,2 @@ +net stop DotNetMQ +C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil -u DotNetMQ.exe diff --git a/binaries/log4net.dll b/binaries/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/binaries/log4net.dll differ diff --git a/doc/Article-latest.htm b/doc/Article-latest.htm new file mode 100644 index 0000000..23a243f --- /dev/null +++ b/doc/Article-latest.htm @@ -0,0 +1,1090 @@ + + + + +Article Source + + + + + + + + +
+ + + + + + + + +

Article Outline

+ + + +

Introduction

+ +

In this article, I will introduce a new and independent Open Source Message Queue system that is entirely built in C# and .NET framework 3.5. DotNetMQ is a message broker that has several features including guaranteed delivering, routing, load balancing, server graphs... so on. I will start by explaining messaging concepts and the need for message brokers. Then I will examine what DotNetMQ is and how to use it.

+ +

What Is Messaging?

+ +

Messaging is a way of asynchronous communication of applications running on same or different machines with reliable delivery. Programs communicate by sending packets of data called messages to each other [1].

+ +

A message may be a string, a byte array, an object... etc. Typically, a sender (producer) program creates a message and pushes it to a message queue and a receiver (consumer) program gets the message from the queue and processes it. The sender and receiver programs don’t have to be running at the same time, since messaging is an asynchronous process. This is called loosely coupled communication.

+ +

On the other hand, a Web Service method call (Remote Method Invocation) is a type of tightly coupled and synchronous communication (both applications have to be running and available during the whole communication; if the Web Service is offline or an error occurs during the method call, the client application gets an exception).

+ +

Message Queue

+ +
Figure - 1: Simplest messaging of two applications.
+ +

In the figure above, two applications communicate over a message queue in a loosely coupled manner. If the receiver consumes messages slower than the sender produces it, the message count on the queue will increase. Also, the receiver may be offline while the sender is sending messages. In this situation, the receiver gets the messages from the queue when it becomes online (when it starts and joins the queue).

+ +

Message Queues are typically provided by Message Brokers. A Message Broker is a standalone application (service) that other applications connect to and send/receive messages. A Message Broker is responsible to store messages until a receiver receives them. A Message Broker can route messages across machines to deliver a message to the destination application and can try delivering the message until the receiver correctly handles it. A Message Broker is sometimes called a Message Oriented Middleware (MOM) or simply Message Queue (MQ).

+ +

What is DotNetMQ?

+ +

DotNetMQ is an open source Message Broker that has several features:

+ +
    +
  • Persistent or non-persistent messaging.
  • + +
  • Guaranteed delivery of persistent messages even in a system crash.
  • + +
  • Automatic and manual routing of messages in a custom machine graph.
  • + +
  • Supports multiple databases (MS SQL Server, MySQL, SQLite, and memory-based storage for now).
  • + +
  • Supports don’t store, direct send style messaging.
  • + +
  • Supports Request/Reply style messaging.
  • + +
  • Easy to use client library to communicate with the DotNetMQ Message Broker.
  • + +
  • Built-in framework to easily construct RMI services upon message queues.
  • + +
  • Supports delivering messages to ASP.NET Web Services.
  • + +
  • GUI-based management and monitoring tool.
  • + +
  • Easy to install, manage, and use.
  • + +
  • Written entirely in C# (using .NET Framework 3.5).
  • +
+ +

I preferred to name DotNetMQ as MDS (Message Delivery System) when first creating it. Because it is designed not just to be a message queue, but also as a system that delivers messages directly to applications and an environment that provides a framework to build application services. I called it DotNetMQ since it is entirely developed using .NET and the DotNetMQ name is more memorable. So, it’s original name (and internal project name) is MDS and the applications have many classes with the prefix MDS.

+ +

Why a New Message Broker?

+ +

The Need for a Message Broker

+ +

First, I will demonstrate a simple situation where a message broker is needed.

+ +

In my experiences in business life, I've observed really bad and uncommon asynchronous enterprise application integration solutions. Usually there is an application that runs on a server and performs some tasks and produces data, and then sends the result data to another application on another server. The second application performs other tasks on the data or evaluates the result (the servers are on the same network or connected over the internet). Also, the message data must be persistent. Even if the remote application is not working or the network is not available, the message must be delivered on the first chance.

+ +

Let’s look at the design in the figure below.

+ +

Bad Messaging

+ +
Figure - 2: A bad solution to integrate applications.
+ +

Application - 1 and Application - 2 are executable applications (or Windows services) and Sender Service is a Windows service. Application - 1 performs some task, produces data, and calls a Remote Web Service method on Server - B to transmit data. This Web Service inserts data into a database table. Application - 2 periodically checks the table for new incoming data rows and processes them (and deletes them from the table or marks them as processed to not process the same data again).

+ +

If an error occurs during the Web Service call or while processing data in the Web Service, data must not be lost and must be sent later. However, Application - 1 has other tasks to do, so it can not try to send data again and again. It simply inserts data into a database table. Another Windows service (or a thread in Application - 1, if the application always runs) checks this table periodically and tries to send data to the Web Service until data is successfully sent.

+ +

This scenario is really reliable (messages are guaranteed to be delivered) but is not an efficient way of communicating between two applications. This solution has some very critical problems:

+ +
    +
  • It takes a long time to develop (to code).
  • + +
  • Individual coding for all message types (or remote method calls). For a new Web Service method call, you must change all the services, applications, and database tables.
  • + +
  • Almost same software and structures must be developed (or copied and modified) for every similar service.
  • + +
  • Testing and maintenance of too many services/applications/databases after coding.
  • + +
  • Some applications and services periodically check the database even if there is no new message (if the database is not well indexed and optimized, this may consume serious system resources).
  • +
+ +

Message Brokers do all this job and takes all the responsibility to deliver messages to the remote application in the most efficient way. The same application integration using DotNetMQ is shown in the figure below.

+ +

Simple DotNetMQ Messaging

+ +
Figure - 3: Simple messaging by using DotNetMQ.
+ +

DotNetMQ is a standalone Windows service that runs on both Server - A and Server - B. Thus, you just need to write code to communicate with DotNetMQ. Using the DotNetMQ Client Library, it is very easy and fast to connect and send/receive messages to/from the DotNetMQ service. Application - 1 prepares the message, sets the destination, and passes the message to the DotNetMQ Broker. DotNetMQ brokers will deliver the message to Application - 2 in the most efficient and fastest way.

+ +

What About Existing Message Brokers

+ +

It is clear to see that there is a need for Message Brokers to integrate applications. I searched the web, and read books to find a free (and Open Source, if available) Message Broker that is easy to use with .NET. Let’s talk about what I found:

+ +
    +
  • Apache ActiveMQ (http://activemq.apache.org): It is Open Source and implements JMS (Java Message Service is a standard API for messaging in the Java world). It has also a .NET client library. I read a complete book “ActiveMQ in Action” to learn more and I developed some simple applications. Even though I read the book, I did not see an easy and reliable way to construct an ActiveMQ server graph that worked together and routed messages. I also did not see a way to set the destination server for a message. It routes messages automatically but I can not control the routing efficiently. I understood that it is commonly used with Apache Camel (http://camel.apache.org) to achieve common application integration patterns. Apache Camel is also another world to discover, and even worse, it is just for Java. Finally, I think that it is not simple enough to use and especially to configure, monitor, and manage it. So I gave up working on ActiveMQ.
  • + +
  • MSMQ (http://msdn.microsoft.com/en-us/library/ms711472(VS.85).aspx): This is a solution from Microsoft and it is the most suitable framework to use with .NET applications. It is easy to use and learn, and it has tools to monitor queues and messages. It is especially very suitable for asynchronous communication of applications that are running on the same machine or can directly connect to the same machine. But I could not find a built-in solution to construct a graph of MSMQ servers that route messages. Since routing is my first start point, I eliminated this Broker.
  • + +
  • RabbitMQ (http://www.rabbitmq.com): It is developed using the Erlang programming platform (that is developed by Ericsson). You need to install Erlang first. I spent a lot of time to install, configure, and write a sample application. It has a .NET client but I got many errors when trying to develop and run a simple application. It was very hard to install and to make two rabbitMQ Brokers work together on two different servers. After a few days, I gave up because I thought it must not be that hard to learn and to start developing applications.
  • + +
  • OpenAMQ (http://www.openamq.org), ZeroMQ (http://www.zeromq.org): I examined these brokers overall but I found that I can not easily do what I want to using .NET.
  • + +
  • Others: I also found a few other projects but they have important features missing like routing, persistent messaging, request/reply messaging... etc.
  • +
+ +

You see that there is no Message Broker that is developed entirely in .NET in the list above.

+ +

From a user perspective, I just want to pass “message data, destination server, and application name” to my local Broker. I am not interested in the rest. It will route a message over the network as many times it requires and delivers the message to my destination application on the destination server. My messaging system must provide this simplicity for me. This was my first start point and I evaluated Message Brokers according to that point. The figure below shows what I want.

+ +

Complete Messaging

+ +
Figure - 4: Automatic routing messages in a Message Broker servers graph.
+ +

Application - 1 passes a message to Message Broker in the local server (Server - A):

+ +
    +
  • Destination server: Server - D
  • + +
  • Destination application: Application - 2
  • + +
  • Message Data: application specific data
  • +
+ +

Server - A has no direct connection to Server - D. So the Message Brokers forward the message over the servers (the message is transmitted through Server - A, Server - B, Server - C, and Server - D sequentially) and the message finally reaches the Message Broker in Server - D to deliver the message to Application - 2. Note that there is another instance of Application - 2 running on Server - E, but it does not receive this message, since the destination server of the message is Server - D.

+ +

DotNetMQ provides this functionality and simplicity. It finds the best (shortest) path from the source server to the destination server on the graph and forwards the message.

+ +

After this comprehensive introduction, let’s see how to use DotNetMQ in practice.

+ +

Installing and Running DotNetMQ

+ +

There is no auto install for now, but it is very easy to install DotNetMQ. download and unzip the binaries download file from the top of the article. Just copy everything from there to C:\Program Files\DotNetMQ\ and run INSTALL_x86.bat (or INSTALL_x64.bat if you are using 64bit operating system).

+

You can check Windows services to see if DotNetMQ is installed and working.

+ +

First Application Using DotNetMQ

+ +

Let’s see DotNetMQ in action. To make the first application most simple, I assume that there are two console applications running on the same machine (in fact (as we will see later in this document) there is no significant difference if the applications are in different machines; the only difference is properly setting the name of the destination server in the message).

+ +
    +
  • Application1: Gets a string message from the user and sends it to Application2.
  • + +
  • Application2: Writes incoming messages to the console screen.
  • +
+ +

Registering Applications to DotNetMQ

+ +

We need to register applications once to use them with DotNetMQ. It is a very simple process. Run DotNetMQ Manager (MDSManager.exe in the DotNetMQ program folder (default: C:\Program Files\DotNetMQ\)), and open Application List from the Applications menu. Click the Add New Application button and enter a name for the application.

+ +

Add the Application1 and Application2 applications to DotNetMQ as described above. Finally, your application list must be like below.

+ +

DotNetMQManager

+ +
Figure - 5: Application list screen of the DotNetMQ Manager tool.
+ +

This screen shows the registered applications to DotNetMQ. The Connected Clients column shows the count of instances of the application that are currently connected to DotNetMQ. It is not needed to restart DotNetMQ because of the changes in this screen.

+ +

Developing Application1

+ +

Create a new console application with name Application1 in Visual Studio and add a reference to MDSCommonLib.dll that provides necessary classes to connect to DotNetMQ. Then write the following code in the Program.cs file:

+ +
using System;
+using System.Text;
+using MDS.Client;
+
+namespace Application1
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            //Create MDSClient object to connect to DotNetMQ
+            //Name of this application: Application1
+            var mdsClient = new MDSClient("Application1");
+
+            //Connect to DotNetMQ server
+            mdsClient.Connect();
+
+            Console.WriteLine("Write a text and press enter to send " + 
+               "to Application2. Write 'exit' to stop application.");
+
+            while (true)
+            {
+                //Get a message from user
+                var messageText = Console.ReadLine();
+                if (string.IsNullOrEmpty(messageText) || messageText == "exit")
+                {
+                    break;
+                }
+
+                //Create a DotNetMQ Message to send to Application2
+                var message = mdsClient.CreateMessage();
+                //Set destination application name
+                message.DestinationApplicationName = "Application2";
+                //Set message data
+                message.MessageData = Encoding.UTF8.GetBytes(messageText);
+
+                //Send message
+                message.Send();
+            }
+
+            //Disconnect from DotNetMQ server
+            mdsClient.Disconnect();
+        }
+    }
+}
+ +

When creating the MDSClient object, we pass the application name which connects to DotNetMQ. With this constructor, we connect to DotNetMQ on the local server (127.0.0.1) with the default port number (10905). Overloaded constructors can be used to connect to another server and port.

+ +

The CreateMessage method of MDSClient returns an object of type IOutgoingMessage. The MessageData property is the actual data to send to the destination application. It is a byte array. We are converting the user input text to a byte array using UTF8 encoding. TheDestinationApplicationName and DestinationServerName properties are used to set the destination address of message. If we don’t specify the destination server, it is assumed as the local server. Finally, we send the message.

+ +

Developing Application2

+ +

Create a new console application with name Application2 in Visual Studio, add a reference to MDSCommonLib.dll and write the following code:

+ +
using System;
+using System.Text;
+using MDS.Client;
+
+namespace Application2
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            //Create MDSClient object to connect to DotNetMQ
+            //Name of this application: Application2
+            var mdsClient = new MDSClient("Application2");
+
+            //Register to MessageReceived event to get messages.
+            mdsClient.MessageReceived += MDSClient_MessageReceived;
+
+            //Connect to DotNetMQ server
+            mdsClient.Connect();
+
+            //Wait user to press enter to terminate application
+            Console.WriteLine("Press enter to exit...");
+            Console.ReadLine();
+
+            //Disconnect from DotNetMQ server
+            mdsClient.Disconnect();
+        }
+
+        /// <summary>
+        /// This method handles received messages from other applications via DotNetMQ.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">Message parameters</param>
+        static void MDSClient_MessageReceived(object sender, MessageReceivedEventArgs e)
+        {
+            //Get message
+            var messageText = Encoding.UTF8.GetString(e.Message.MessageData);
+
+            //Process message
+            Console.WriteLine();
+            Console.WriteLine("Text message received : " + messageText);
+            Console.WriteLine("Source application    : " + e.Message.SourceApplicationName);
+
+            //Acknowledge that message is properly handled
+            //and processed. So, it will be deleted from queue.
+            e.Message.Acknowledge();
+        }
+    }
+}
+ +

Creating the MDSClient object is similar to that in Application1 but the application name is Application2. To receive messages for an application, it needs to register to the MessageReceived event of MDSClient. Then we connect to DotNetMQ and stay connected until the user presses Enter.

+ +

When a message is sent to Application2, the MDSClient_MessageReceived method handles the event. We get the message from the Message property of MessageReceivedEventArgs. The type of message is IIncomingMessage. The MessageData property of IIncomingMessage contains the actual message data that is sent by Application1. Since it is a byte array, we are converting it to string using UTF8 encoding. We write the message text that is sent by Application1 to the console screen.

+ +

First Apps

+ +
Figure - 6: Application1 sends two messages to Application2 over DotNetMQ.
+ +

After processing an incoming message, it is needed to Acknowledge the message. That means the message is properly received and correctly processed. DotNetMQ then removes the message from message queue. We can also reject a message using the Reject method (if we can not process the message on an error case). In this situation, the message turns back to the message queue and will be sent later to the destination application (or it will be sent to another instance of Application2 on the same server if exists). This is a powerful mechanism of the DotNetMQ system. Thus, it is guarantied that the message can not be lost and it is absolutely processed. If you do not acknowledge or reject a message, it is assumed as rejected. So, even if your application crashes, your message is sent back to your application later.

+

If you run multiple instance of the Application2, which one will receive messages? In this case, DotNetMQ delivers messages to applications sequentially. So, you can create multi sender/receiver systems. A message is received by only one instance of applications (applications receives different messages). DotNetMQ provides all functionallity and synchronization.

+ +

Transmit Rule Property of Message

+ +

Before sending a message, you can set the Transmit Rule of a message like this:

+ +
message.TransmitRule = MessageTransmitRules.NonPersistent;
+ +

There are three types of transmit rules:

+ +
    +
  • StoreAndForward: This is the default transmit rule. Messages are persistent, can not be lost, and are guaranteed to be delivered. If the Send method does not throw an Exception, then the message is correctly received by DotNetMQ and stored in the database. It is stored in the database until the destination application receives and acknowledges it.
  • + +
  • NonPersistent: Messages are not stored in database. It is the fastest way of sending messages. A message is lost only if the DotNetMQ server stops.
  • + +
  • DirectlySend: This is an exclusive feature of DotNetMQ. This type of messages are directly sent to the application. The sender application is blocked until the receiver acknowledges a message. So, if the sender does not get any exception while calling the Send method, it means the message is properly received and acknowledged by the receiver application. If an error occurs while transmitting a message, the receiver is offline, or the receiver rejects the message, the sender gets an exception on the Send method. This rule correctly works even if applications are on different servers (even if there are many servers between applications).
  • +
+ +

Since the default transmit rule is StoreAndForward, let’s try that:

+ +
    +
  • Run Application1 (while Application2 is not running), write some messages, and close application.
  • + +
  • Run Application2, you will see that your messages are received by Application2 and are not lost.
  • +
+ +

Even if you stop the DotNetMQ service from Windows services after sending messages from Application1, your messages won’t be lost. That is called persistence.

+ +

CommunicationWay Property of MDSClient

+ +

By default, an application can send and receive messages using MDSClient (CommunicationWays.SendAndReceive). If an application doesn’t want to receive messages, it must set the CommunicationWay property to CommunicationWays.Send. This property can be changed before connection or during communication with DotNetMQ.

+ +

ReConnectServerOnError Property of MDSClient

+ +

By default, MDSClient automatically reconnects to DotNetMQ if it disconnects. So, even if you restart DotNetMQ, it is not needed to restart your applications that are connected to DotNetMQ. You can set the ReConnectServerOnError property to false to disable auto-reconnect.

+

AutoAcknowledgeMessages Property of MDSClient

+

By default, You must explicitly acknowledge messages in MessageReceived event. Otherwise, it is assumed as Rejected. If you want oppisite of this approach, you must set AutoAcknowledgeMessages property as true. In this case, if your MessageReceived event handler does not throw an exception or you do not acknowledge/reject the message explicitly, it is automatically acknowledged (If an exception is thrown, the message is rejected).

+ +

Configuring DotNetMQ

+ +

You can configure DotNetMQ in two ways: Using XML settings files or DotNetMQ Manager (Windows Forms application). Here, I will show two approaches. Some of the configurations require you to restart DotNetMQ while others do not.

+ +

Servers

+ +

You may run DotNetMQ on only one server. In this situation, there is no need to configure anything for the servers. But if you want to run DotNetMQ on more than one server and make them communicate with others, you must define your server graph.

+ +

A server graph consists of two or more nodes. Each node is a server that has an IP address and TCP port (that is used by DotNetMQ). You can configure/design a server graph by using the DotNetMQ Manager.

+ +

Server Graph

+ +
Figure - 8: DotNetMQ Server Graph managing.
+ +

In the figure above, you see a server graph that consists of five nodes. The red node represents this server (this server means the server that you are connected with DotNetMQ Manager). A line means that there is a connection (and they can send/receive messages) between two nodes (they are called adjacent nodes). The name of the server/node in a graph is important and is used when sending messages to the server.

+ +

You can double-click a server in the graph to change its properties. To connect two servers, hold Ctrl, click the first one then the second one (to disconnect, do the same thing again). You can set a server as this server by right clicking and selecting Set as this server. You can also delete a server from the graph or add a new server by using the right click menu. Lastly, you can add move servers by dragging.

+ +

After designing your server graph, you must click the Save & Update Graph button to save the changes. The changes are saved to the MDSSettings.xml file in your DotNetMQ installation folder. You must restart DotNetMQ to apply the changes.

+ +

For the server graph above, the corresponding MDSSettings.xml settings are shown below:

+ +
<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  <Settings>
+    ...
+  </Settings>
+  <Servers>
+    <Server Name="halil_pc" IpAddress="192.168.10.105" 
+       Port="10099" Adjacents="emre_pc" />
+    <Server Name="emre_pc" IpAddress="192.168.10.244" Port="10099" 
+       Adjacents="halil_pc,out_server,webserver1,webserver2" />
+    <Server Name="out_server" IpAddress="85.19.100.185" 
+       Port="10099" Adjacents="emre_pc" />
+    <Server Name="webserver1" IpAddress="192.168.10.263" 
+       Port="10099" Adjacents="emre_pc,webserver2" />
+    <Server Name="webserver2" IpAddress="192.168.10.44" 
+       Port="10099" Adjacents="emre_pc,webserver1" />
+  </Servers>
+  <Applications>
+    ...
+  </Applications>
+  <Routes>
+    ...
+  </Routes>
+</MDSConfiguration>
+ +

Surely, this configuration is made according to your real network. You must install DotNetMQ on all servers in the graph. Also, you must configure the same graph on all servers (you can easily copy the server nodes from the XML to the other servers).

+ +

DotNetMQ uses a short path algorithm to send the messages (if no manual route is defined in the settings file). Consider an Application A that is running on halil_pc and sending a message to Application B on webserver2. The path is simply: Application A -> halil_pc -> emre_pc -> webserver2 -> Application B. halil_pc knows the next forwarding server (emre_pc) by using the server graph definition.

+ +

Lastly, the MDSSettings.design.xml file contains the server design information (locations of nodes on the screen). This is just needed in the server graph window in DotNetMQ Manager and not needed for the runtime of DotNetMQ.

+ +

Applications

+ +

As shown in Figure - 5, you can add/remove applications that are using DotNetMQ as a message broker. It is not needed to restart DotNetMQ for these changes. Application settings are also saved to the MDSSettings.xml file as shown below.

+ +
<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  ...
+  <Applications>
+    <Application Name="Application1" />
+    <Application Name="Application2" />
+  </Applications>
+  ...
+</MDSConfiguration>
+ +

An application must be in this list to be able to connect to DotNetMQ. If you directly change the XML file, you must restart the DotNetMQ server.

+ +

Routing / Load Balancing

+ +

A usable feature of DotNetMQ is routing. Routing settings (for now) are configured only in the XML settings file (MDSSettings.xml). You can see two types of routing in the settings file below:

+ +
<?xml version="1.0" encoding="utf-8" ?>
+<MDSConfiguration>
+  ...
+  <Routes>
+
+    <Route Name="Route-App2" DistributionType="Sequential" >
+      <Filters>
+        <Filter DestinationServer="this" DestinationApplication="Application1" />
+      </Filters>
+      <Destinations>
+        <Destination Server="Server-A" Application="Application1" RouteFactor="1" />
+        <Destination Server="Server-B" Application="Application1" RouteFactor="1" />
+        <Destination Server="Server-C" Application="Application1" RouteFactor="1" />
+    </Destinations>
+    </Route>
+
+    <Route Name="Route-App2" DistributionType="Random" >
+      <Filters>
+        <Filter DestinationServer="this" DestinationApplication="Application2" /> 
+        <Filter SourceApplication="Application2" TransmitRule="StoreAndForward" /> 
+    </Filters>
+      <Destinations>
+        <Destination Server="Server-A" Application="Application2" RouteFactor="1" />
+        <Destination Server="Server-B" Application="Application2" RouteFactor="3" />
+      </Destinations>
+    </Route>
+    
+  </Routes>
+  ...
+</MDSConfiguration>
+ +

A Route node has two attribute: Name is a user-friendly name of the Route entry (does not affect routing) and DistributionType is the strategy of the routing. There are two types of routing strategies:

+ +
    +
  • Sequential: Messages are routed to destination servers sequentially. The RouteFactor of destinations are considered while distributing.
  • + +
  • Random: Messages are routed to destination servers randomly. Probability of selecting the server A is: (RouteFactor(A) / (Total of all RouteFactor values of all destinations in the route definition)).
  • +
+ +

Filters are used to decide which route to use for a message. If properties of a message are suitable for one of the filters, the message is routed. There are five conditions (XML attributes) to define a filter:

+ +
    +
  • SourceServer: The first source server of the message. Can be this to indicate this server.
  • + +
  • SourceApplication: Sender application of the message.
  • + +
  • DestinationServer: Last destination server of the message. Can be this to indicate this server.
  • + +
  • DestinationApplication: The application that will receive the message.
  • + +
  • TransmitRule: One of these transmit rules: StoreAndForward, DirectlySend, or NonPersistent.
  • +
+ +

If one or more condition is not declared, it is not considered while filtering messages. So, if all the conditions are empty (or not declared), all messages are fit to this filter. A filter is selected for a message only if all the conditions are fit to the message. If a message is proper for (at least) one of the filters of a route, the route is selected and used.

+ +

Destinations are used to route messages to other servers. One of the destinations is selected according to the DistributionType property of the Route entry (explained before). A destination must define three attributes:

+ +
    +
  • Server: Destination server. Can be this to indicate this server.
  • + +
  • Application: Destination application. Destination application is generally defined as same as the original destination, but you can redirect a message to another application than its original destination application.
  • + +
  • RouteFactor: This property is used to indicate the relative selection ratio of a destination. The RouteFactor attribute can be used for load balancing. If you want to distribute messages to all servers equally, you can define this as 1 for all destinations. But if you have two servers and one of them is more powerful than other, you can select the first server twice more than the second one by defining the appropriate route factors.
  • +
+ +

You must restart DotNetMQ after changing routes.

+ +

Other Settings

+ +

DotNetMQ currently supports three storage types: SQLite (default), MySQL, and Memory. You can change the storage type in the MDSSettings.xml file.

+ +
<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  ...
+  <Settings>
+    <Setting Key="ThisServerName" Value="halil_pc" />
+    <Setting Key="StorageType" Value="SQLite" />
+  </Settings>
+  ...
+</MDSConfiguration>
+ +

The storage types must be one of the following values:

+ +
    +
  • SQLite: Uses the SQLite database system. This is the default storage type. Uses the {DotNetMQ-Install-Directory}\SqliteDB\MDS.s3db file as the database.
  • +
  • MSSQL: Uses a Microsoft SQL Server database. You must supply the ConnectionString setting as the connection string (will be explained).
  • + +
  • MySQL-ODBC: Uses a MySQL database with ODBC. You must supply the ConnectionString setting as the connection string.
  • + +
  • MySQL-Net: Uses a MySQL database with a .NET Adapter. You must supply the ConnectionString setting as connection string.
  • + +
  • Memory: Uses memory as the storage device. In this case, persistent messages are lost if DotNetMQ is stopped.
  • +
+ +

Here is a sample configuration to use the MySQL-ODBC storage type:

+ +
<Settings>
+    <Setting Key="ThisServerName" Value="halil_pc" />
+    <Setting Key="StorageType" Value="MySQL-ODBC" />
+    <Setting Key="ConnectionString" 
+       Value="uid=root;server=localhost;driver={MySQL ODBC 3.51 Driver};database=mds" />
+  </Settings>
+ +

You can find needed files in the Setup\Databases folder (in the DotNetMQ installation folder) that are needed to create database and tables that are used by DotNetMQ. Feel free to ask questions to me if you have a problem.

+

There is also another setting to define the name of the current/this server (ThisServerName). It must be one of the servers in the Servers section. If you use the DotNetMQ Manager to edit your servers graph, it is automatically set.

+

Messaging Over Network

+ +

Sending a message to an application on a remote server is easy as sending a message to an application on the current server.

+ +

A Simple Application

+ +

Let's consider the network below.

+ +

Messaging Over Network

+ +
Figure - 8: Messaging of two applications over network with DotNetMQ.
+ +

There is an application (Application1) running on ServerA that wants to send a message to another application (Application2) on ServerC and there is no direct connection between ServerA and ServerC because of the firewall rules. Let's change the applications we developed in the First Applications section.

+ +

There is not even a single change in Application2. Just run Application2 in ServerC and wait for incoming messages.

+ +

There is a minor change in Application1 on how we send a message. It must set the DestinationServerName of the message as ServerC.

+ +
var message = mdsClient.CreateMessage();
+message.DestinationServerName = "ServerC"; //Set destination server name here!
+message.DestinationApplicationName = "Application2";
+message.MessageData = Encoding.UTF8.GetBytes(messageText);
+message.Send();
+ +

That's all. You do not have to know where ServerC is, for a direct connection to ServerC... They are all defined in the DotNetMQ settings. Note that if you do not set the DestinationServerName of a message, it is assumed as current/this server and DotNetMQ sends the message to the application on the same server. Also, if you define the necessary routings, you don't have to set the destination server: it is routed by DotNetMQ automatically.

+ +

Surely, DotNetMQ settings must be properly set according to the server connections (server graph), and Application1 and Application2 must be registered to the DotNetMQ server as described in the Configuring DotNetMQ section.

+ +

A Real Life Case: Distributed SMS Processor

+ +

As you already saw, DotNetMQ can be used to build distributed, load balanced application systems. In this section, I'll discuss a real life scenario: A distributed SMS process system.

+ +

Assume that there is a short message (SMS) service that is used for polling a music competition. After all competitors sing their songs, audience send messages like "VOTE 103" to our SMS service to vote for their favourite competitor (103 is a sample code to vote for a specific competitor). And assume that this polling is done in just 30 minutes and approximately five million people will send SMSs to our service.

+ +

We will receive every message, process it (parse the SMS text, update the database to increase the vote count of the competitor) and send a confirmation message to the sender of the SMS. We must receive messages from two servers, process messages on four servers, and send confirmation messages from two servers. We have totally eight servers. Let's see our complete system diagram:

+ +

SMS System

+ +
Figure - 9: A distributed SMS processing system
+ +

There are three types of applications: Receiver, Processor, and Sender. You can use DotNetMQ as the message queue and load balancer in such a scenario to build a distributed, scalable message processing system by configuring the server graph and routes as described in the Configuring DotNetMQ section.

+ +

Request/Reply Style Messaging with DotNetMQ

+ +

In most cases, an application sends a message to another application and gets a response message. DotNetMQ has built-in support for this type of messaging. Think about a service that is used to query a stock status. There are two types of messages:

+ +
[Serializable]
+public class StockQueryMessage
+{
+    public string StockCode { get; set; }
+}
+
+[Serializable]
+public class StockQueryResultMessage
+{
+    public string StockCode { get; set; }
+    public int ReservedStockCount { get; set; }
+    public int TotalStockCount { get; set; }
+}
+ +

A simple Stock server code is shown below.

+ +
using System;
+using MDS;
+using MDS.Client;
+using StockCommonLib;
+
+namespace StockServer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            var mdsClient = new MDSClient("StockServer");
+            mdsClient.MessageReceived += MDSClient_MessageReceived;
+
+            mdsClient.Connect();
+
+            Console.WriteLine("Press enter to exit...");
+            Console.ReadLine();
+
+            mdsClient.Disconnect();
+        }
+
+        static void MDSClient_MessageReceived(object sender, 
+                    MessageReceivedEventArgs e)
+        {
+            //Get message
+            var stockQueryMessage = 
+                GeneralHelper.DeserializeObject(e.Message.MessageData) 
+                as StockQueryMessage;
+            if (stockQueryMessage == null)
+            {
+                return;
+            }
+
+            //Write message content
+            Console.WriteLine("Stock Query Message for: " + 
+                              stockQueryMessage.StockCode);
+
+            //Get stock counts from a database...
+            int reservedStockCount;
+            int totalStockCount;
+            switch (stockQueryMessage.StockCode)
+            {
+                case "S01":
+                    reservedStockCount = 14;
+                    totalStockCount = 80;
+                    break;
+                case "S02":
+                    reservedStockCount = 0;
+                    totalStockCount = 25;
+                    break;
+                default: //Stock does not exists!
+                    reservedStockCount = -1;
+                    totalStockCount = -1;
+                    break;
+            }
+
+            //Create a reply message for stock query
+            var stockQueryResult = new StockQueryResultMessage
+                                       {
+                                           StockCode = stockQueryMessage.StockCode,
+                                           ReservedStockCount = reservedStockCount,
+                                           TotalStockCount = totalStockCount
+                                       };
+            
+            //Create a MDS response message to send to client
+            var responseMessage = e.Message.CreateResponseMessage();
+            responseMessage.MessageData = 
+               GeneralHelper.SerializeObject(stockQueryResult);
+
+            //Send message
+            responseMessage.Send();
+
+            //Acknowledge the original request message.
+            //So, it will be deleted from queue.
+            e.Message.Acknowledge();
+        }
+    }
+}
+ +

The stock server listens for incoming StockQueryMessage objects and sends a StockQueryResultMessage to the sender. To be simple, I did not select stocks from a database. A response message is created by the CreateResponseMessage() method of the incoming message. Lastly, the message is acknowledged after a response is sent. Now I will show a simple stock client code to get stock information from a server:

+ +
using System;
+using MDS;
+using MDS.Client;
+using MDS.Communication.Messages;
+using StockCommonLib;
+
+namespace StockApplication
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Console.WriteLine("Press enter to query a stock status");
+            Console.ReadLine();
+
+            //Connect to DotNetMQ
+            var mdsClient = new MDSClient("StockClient");
+            mdsClient.MessageReceived += mdsClient_MessageReceived;
+            mdsClient.Connect();
+
+            //Create a stock request message
+            var stockQueryMessage = new StockQueryMessage { StockCode = "S01" };
+            
+            //Create a MDS message
+            var requestMessage = mdsClient.CreateMessage();
+            requestMessage.DestinationApplicationName = "StockServer";
+            requestMessage.TransmitRule = MessageTransmitRules.NonPersistent;
+            requestMessage.MessageData = GeneralHelper.SerializeObject(stockQueryMessage);
+
+            //Send message and get response
+            var responseMessage = requestMessage.SendAndGetResponse();
+
+            //Get stock query result message from response message
+            var stockResult = (StockQueryResultMessage) 
+              GeneralHelper.DeserializeObject(responseMessage.MessageData);
+
+            //Write stock query result
+            Console.WriteLine("StockCode          = " + stockResult.StockCode);
+            Console.WriteLine("ReservedStockCount = " + stockResult.ReservedStockCount);
+            Console.WriteLine("TotalStockCount    = " + stockResult.TotalStockCount);
+
+            //Acknowledge received message
+            responseMessage.Acknowledge();
+
+            Console.ReadLine();
+
+            //Disconnect from DotNetMQ server.
+            mdsClient.Disconnect();
+        }
+
+        static void mdsClient_MessageReceived(object sender, 
+                    MessageReceivedEventArgs e)
+        {
+            //Simply acknowledge other received messages
+            e.Message.Acknowledge();
+        }
+    }
+}
+ +

In the sample above, TransmitRule is selected as NonPersistent to show a sample usage. Surely, you can send StoreAndForward (persistent) messages. Here is a sample screenshot of the running applications:

+ +

Sample Request Reply Messaging

+ +
Figure - 10: Request/Reply style messaging applications.
+ +

Service-Oriented Architecture in DotNetMQ

+ +

SOA (Service-Oriented Architecture) has been a popular concept for many years. Web Services and WCF are two major solutions to SOA. Generally, it is not expected to support SOA from a Message Queue system. Also messaging is an asynchronous and loosely coupled process while a Web Service method call is typically synchronous and tight coupled. Even (as you saw in the previous sample applications) messaging is not as easy as calling a remote method. But when your message count increases, your application becomes complicated and harder to maintain.

+ +

DotNetMQ supports remote method invocation mechanism upon persistent or non-persistent messages. So you can call a remote method asynchronously that is guaranteed to be called and guaranteed to be succeed!

+ +

Sample Application: SMS/Mail Sender

+ +

Here we will develop a simple service that can be used to send SMS and e-mail. Maybe it is not needed to write a service for sending an email/SMS, all applications can do it themselves. But imagine that you have many applications that are sending emails. What if the mail server has a problem while sending an email? The application must try until it successfully sends the email. So you must build a queue mechanism in your application to try and send the email again and again. At the worse case, your application may be a short time running application (such as a Web Service) or must be closed before sending the email. But you have to send the email when the mail servers come online and the mail must not be lost.

+

In this case, you can develop a separate mail/SMS service that will try to send the SMS/mail until it is successfully sent. You can develop a mail service that receives mail requests over DotNetMQ and acknowledge requests (messages) only if the email is successfully sent. If sending fails, just do not acknowledge (or reject) the message, thus it will be tried again later.

+ +

Service

+ +

We will first develop the mail/SMS service. To do this, we must define a class that is delivered from the MDSService base class:

+ +
using System;
+using MDS.Client.MDSServices;
+
+namespace SmsMailServer
+{
+    [MDSService(Description = "This service is a " + 
+              "sample mail/sms service.", Version = "1.0.0.0")]
+    public class MyMailSmsService : MDSService
+    {
+        //All parameters and return values can be defined.
+        [MDSServiceMethod(Description = "This method is used send an SMS.")]
+        public void SendSms(
+            [MDSServiceMethodParameter("Phone number to send SMS.")] string phone,
+            [MDSServiceMethodParameter("SMS text to be sent.")] string smsText)
+        {
+            //Process SMS
+            Console.WriteLine("Sending SMS to phone: " + phone);
+            Console.WriteLine("Sms Text: " + smsText);
+
+            //Acknowledge the message
+            IncomingMessage.Acknowledge();
+        }
+
+        //You do not have to define any parameters
+        [MDSServiceMethod]
+        public void SendEmail(string emailAddress, string header, string body)
+        {
+            //Process email
+            Console.WriteLine("Sending an email to " + emailAddress);
+            Console.WriteLine("Header: " + header);
+            Console.WriteLine("Body  : " + body);
+
+            //Acknowledge the message
+            IncomingMessage.Acknowledge();
+        }
+
+        // A simple method just to show return values.
+        [MDSServiceMethod]
+        [return: MDSServiceMethodParameter("True, if phone number is valid.")]
+        public bool IsValidPhone([MDSServiceMethodParameter(
+               "Phone number to send SMS.")] string phone)
+        {
+            //Acknowledge the message
+            IncomingMessage.Acknowledge();
+            
+            //Return result
+            return (phone.Length == 10);
+        }
+    }
+}
+ +

As you can see, it is just a regular C# class decorated with attributes. The MDSService and MDSServiceMethod attributes must be defined and all other attributes are optional (but it is good to write them). We will see soon why they are used). Your service methods must have the MDSServiceMethod attribute. If you do not want to expose some of your methods, simply do not add the MDSServiceMethod attribute.

+ +

We must also acknowledge the message in the service method. Otherwise, the message (that caused this method call) will not be deleted from the message queue and our method will be called again. We can also reject the message if we can not process it (for example, if the mail server is not working and we can not send emails). If we reject the message, it will be sent to us later (reliability). You can reach the original message using the IncomingMessage property of the MDSService class. Also, you can get a remote application's (that called the service method) information using the RemoteApplication property.

+ +

After creating a proper service class, we must create an application to run it. Here is a simple console application that runs our MyMailSmsService:

+ +
using System;
+using MDS.Client.MDSServices;
+
+namespace SmsMailServer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            using (var service = 
+                      new MDSServiceApplication("MyMailSmsService"))
+            {
+                service.AddService(new MyMailSmsService());
+                service.Connect();
+
+                Console.WriteLine("Press any key to stop service");
+                Console.ReadLine();
+            }
+        }
+    }
+}
+ +

As you can see, it is just three lines of code to create and run a service. Since MDSService is disposable, you can use a using statement. Also, you can close the service manually with the Disconnect method of MDSServiceApplication. You can run more than one service on a single MDSServiceApplication using the AddService method.

+ +

Client

+ +

To develop an application that uses a DotNetMQ service, you must create a service proxy (like Web Services and WCF). To do this, you can use the MDSServiceProxyGenerator tool. First, compile your service project, than run MDSServiceProxyGenerator.exe (in the DotNetMQ installation folder).

+ +

Proxy Generating

+ +
Figure - 11: Generating a proxy class for a service in DotNetMQ.
+ +

Select your service assembly file (SmsMailServer.exe in this sample project). You can select the service class or generate proxies of all services in a given assembly. Enter a namespace and the target folder to generate the proxy class. After generating the proxy class, you can add it to your project.

+ +

I will not show the internals of this proxy class and you do have to know it (you can see it in the source code, it is a very simple class). Your method/parameter attributes are used to generate the code comments in this proxy file.

+ +

After adding the generated proxy class to our project, we can simply send messages to the service just like simple method calls:

+ +
using System;
+using MDS.Client;
+using MDS.Client.MDSServices;
+using SampleService;
+
+namespace SmsMailClient
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Console.WriteLine("Press enter to test SendSms method");
+            Console.ReadLine();
+
+            //Application3 is name of an application that sends sms/email.
+            using (var serviceConsumer = new MDSServiceConsumer("Application3"))
+            {
+                //Connect to DotNetMQ server
+                serviceConsumer.Connect();
+
+                //Create service proxy to call remote methods
+                var service = new MyMailSmsServiceProxy(serviceConsumer, 
+                    new MDSRemoteAppEndPoint("MyMailSmsService"));
+
+                //Call SendSms method
+                service.SendSms("3221234567", "Hello service!");
+            }
+        }
+    }
+}
+ +

You can also call other methods of the service, and get return values as regular method calls. Actually, your method calls are translated to reliable messages. For instance, even if the remote application (MyMailSmsService) is not running when SendSms is called, it is called when the service starts to run, so your method calls are also guarantied to be called.

+ +

You can change the transmit rule for messaging using the TransmitRule property of the service proxy. If a service method returns void, its transmit rule is StoreAndForward by default. If a service method returns a value, the method call can not be made reliable (since the method call is synchronous and waiting a result), it's rule is DirectlySend.

+ +

You can choose any type as method parameters. If it is a primary type (string, int, byte...) there is no need for additional settings but if you want to use your own classes as a method parameter, the class must be marked as Serializable since DotNetMQ uses binary serialization for parameters.

+ +

Note that you must register MyMailSmsService and Application3 to DotNetMQ before running this sample.

+ +

Web Services Support

+ +

Surely, you can connect to DotNetMQ in a Web Service since it is also a .NET application. But, what if you want to write an ASP.NET Web method to handle messages for an application (and can reply with a message in the same context)? Web Services are suitable for such Request/Reply style method calls.

+ +

DotNetMQ supports ASP.NET web services and can deliver messages to Web Services. There is a template web service in samples (in download files) to accomplish that. It is defined as below:

+ +
using System;
+using System.Web.Services;
+using MDS.Client.WebServices;
+
+[WebService(Namespace = "http://www.dotnetmq.com/mds")]
+[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
+public class MDSAppService : WebService
+{
+    /// <summary>
+    /// MDS server sends messages to this method.
+    /// </summary>
+    /// <param name="bytesOfMessage">Byte array form of message</param>
+    /// <returns>Response message to incoming message</returns>
+    [WebMethod(Description = "Receives incoming messages to this web service.")]
+    public byte[] ReceiveMDSMessage(byte[] bytesOfMessage)
+    {
+        var message = WebServiceHelper.DeserializeMessage(bytesOfMessage);
+        try
+        {
+            var response = ProcessMDSMessage(message);
+            return WebServiceHelper.SerializeMessage(response);
+        }
+        catch (Exception ex)
+        {
+            var response = message.CreateResponseMessage();
+            response.Result.Success = false;
+            response.Result.ResultText = 
+              "Error in ProcessMDSMessage method: " + ex.Message;
+            return WebServiceHelper.SerializeMessage(response);
+        }
+    }
+
+    /// <summary>
+    /// Processes incoming messages to this web service.
+    /// </summary>
+    /// <param name="message">Message to process</param>
+    /// <returns>Response Message</returns>
+    private IWebServiceResponseMessage 
+            ProcessMDSMessage(IWebServiceIncomingMessage message)
+    {
+        //Process message
+
+        //Send response/result
+        var response = message.CreateResponseMessage();
+        response.Result.Success = true;
+        return response;
+    }
+}
+ +

You do not change the ReceiveMDSMessage method and must process the message in the ProcessMDSMessage method as shown above. Also, you must define the address of your Web Service in MDSSettings.xml as shown below. You can also add Web Services using the DotNetMQ management tool.

+ +
  ... 
+  <Applications>
+    <Application Name="SampleWebServiceApp">
+      <Communication Type="WebService" 
+        Url="http://localhost/SampleWebApplication/SampleService.asmx" />
+    </Application>
+  </Applications>
+  ... 
+ +

Performance of DotNetMQ

+ +

There are some test results for messaging in DotNetMQ:

+ +

Messaging:

+ +
    +
  • 10,000 messages in ~25 seconds as persistent (~400 messages/second).
  • + +
  • 10,000 messages in ~3.5 seconds as non-persistent (~2,850 messages/second).
  • +
+ +

Method Calls (in DotNetMQ Services)

+ +
    +
  • 10,000 method calls in ~25 seconds as persistent (~400 calls/second).
  • + +
  • 10,000 method calls in ~8.7 seconds as non-persistent (~1,150 calls/second).
  • +
+ +

Test Platform: Intel Core 2 Duo 3,00 GHz CPU. 2 GB RAM PC. Messages/calls are made between two applications running on the same computer.

+ +

References

+ +
    +
  • [1] Book: Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions by Gregor Hohpe, Bobby Woolf (Addison Wesley, 2003).
  • +
+ +

History

+ +
    +
  • 23.05.2011 + (DotNetMQ v0.9.1.0) +
      +
    • Added Microsoft SQL Server database support to store messages.
    • +
    • Changed MySQLConnectionString setting to ConnectionString.
    • +
    • Source codes updated.
    • +
    • Article is updated according to changes.
    • +
    +
  • +
  • 16.05.2011 (DotNetMQ v0.9.0.0) +
      +
    • Added sample web service template to downloads.
    • +
    • Some fixes and additions to the article.
    • +
    +
  • +
  • 09.05.2011 (DotNetMQ v0.9.0.0)
  • + +
      +
    • First published.
    • +
    +
+ +
+ + + + + +
+ + diff --git a/doc/Article.html b/doc/Article.html new file mode 100644 index 0000000..931bcc4 --- /dev/null +++ b/doc/Article.html @@ -0,0 +1,825 @@ + + + + +Untitled Document + + + + +

DotNetMQ: A Complete Message Queue System For .NET

+

Article Outline

+ +

Introduction

+

In this article, I will introduce a new and independed open source Message Queue system that is entirely build in C# and .NET framework 3.5. DotNetMQ is a message broker that has several futures including guaranteed delivering, routing, load balancing, server graphs... so on. I will start by explaining messaging concepts and need for message brokers. Then I will examine what DotNetMQ is and how to use it.

+

What Is Messaging?

+

Messaging is a way of asynchronous communication of applications running on same or different machines with reliable delivery. Programs communicate by sending packets of data called messages to each other [1].

+

A message may be a string, a byte array, an object... etc. Typically, a sender (producer) program creates a message and pushes it to a message queue and a receiver (consumer) program gets the message from queue and processes it. Sender and receiver programs don’t have to be running at the same time, since messaging is an asynchronous process. This is called loosely coupled communication.

+

On the other hand, a web service method call (Remote method invocation) is a type of tightly coupled and synchronous communication (Both of two applications have to be running and available during whole communication. If web service is offline or an error occures during method call, client application gets an exception).

+

Message Queue
+ Figure – 1: Simplest messaging of two applications.

+

In the figure above, two applications communicate over a message queue in a loosely coupled manner. If receiver consumes messages slower than sender produces it, message count on queue will increase. Also, receiver may be offline while sender is sending messages. In this situation, receiver gets messages from queue when it becomes online (when it starts and joins the queue).

+

Message Queues are typically provided by Message Brokers. A Message Broker is a standalone application (service) that other applications connect it and send/receive messages. Message Broker is respobsible to store messages until a receiver receives them. A Message Broker can route messages accross machines to deliver message to the destination application and can try delivering the message until receiver correctly handles it. Message Broker is sometimes called as Message Oriented Middleware (MOM) or simply Message Queue (MQ).

+

What Is DotNetMQ?

+

DotNetMQ is an open source Message Broker that has several features:

+
    +
  • Persistent or non-persistent messaging.
  • +
  • Guaranteed delivery of persistent messages even in a system crash.
  • +
  • Automatic and manual routing of messages in a custom machine graph.
  • +
  • Supports multiple databases (MySQL, SQLite and Memory-based storage for now).
  • +
  • Supports don’t store, direct send style messaging.
  • +
  • Supports Request/Reply style messaging.
  • +
  • Easy to use client library to communicate with DotNetMQ Message Broker.
  • +
  • Built-in framework to easily construct RMI services upon message queues.
  • +
  • Supports delivering messages to ASP.NET web services.
  • +
  • GUI-based management and monitoring tool.
  • +
  • Easy to install, manage and use.
  • +
  • Written entirely in C# (using .NET Framework 3.5).
  • +
+

I prefered to name DotNetMQ as MDS (Message Delivery System) when first creating it. Because it is designed not to be a message queue only, but also a system that delivers messages directly to applications and an environment that provides a framework to built application services. I called it as DotNetMQ since it is entirely developed using .NET and the DotNetMQ name is more memorable. So, it’s original name (and internal project name) is MDS and the applications have many classes with prefix MDS.

+

Why A New Message Broker?

+

The need for a Message Broker

+

First, i will demonstrate a simple situation where a message broker is needed.

+

In my experiences in business life, i observed too bad and not uncommon asynchronous enterprise application integration solutions. Usually there is an application runs on a server, performs some tasks and produces a data, then sends the result data to another application on another server. Second application performs another tasks on data or evaluates the result (Servers are on same network or connected over internet). Also, message data must be persistent. Even if remote application is not working or network is not available, message must be delivered on first chance.

+

Let’s look at the design at the figure below.

+

Bad Messaging
+ Figure – 2: A bad solution to integrate applications.

+


+ Application – 1 and Application – 2 are executable applications (or Windows services) and Sender Service is a Windows service. Application – 1 performs some task, produces a data and calls a Remote Web Service method on Server – B to transmit data. This web service inserts data into a database table. Application – 2 periodically checks table for new incoming data rows and process them if exists (and deletes them from table or marks them as processed to do not process same data again).

+

If an error occurs during web service call or while processing data in web service, data must not be lost and must be sent later. However, Application – 1 has other tasks to do, so it can not try to send data again and again. It simply inserts data into a database table. Another windows service (or a thread in Application – 1, if application always runs) checks this table periodically and tries to send data to web service until data is successfully sent.

+

This scenario is really reliable (message are guaratied to be delivered) but worst efficient way of communicating two applications. This solution has very critical problems:

+
    +
  • It takes long time to develop (to code).
  • +
  • Individual coding for all message types (or remote method calls). For a new web service method call, you must change all services, applications and database tables.
  • +
  • Almost same softwares and structures must be delevoped (or copied and modified) on every similar service.
  • +
  • Test and Maintance of too many services/applications/databases after coding.
  • +
  • Some applications and services periodically checks database even if there is no new message (Especially if database is not well indexed and optimized it may consume serious system recource).
  • +
+

Message Brokers does all job and takes all resposibility to deliver messages to remote application in most efficient way. Same application integration using DotNetMQ is shown in the figure below.

+

Simple DotNetMQ Messaging
+ Figure – 3: Simple messaging by using DotNetMQ.

+

DotNetMQ is ready and standalone Windows service that is running on both of Server – A and Server – B. Thus, it is just needed to write codes to communicate with DotNetMQ. Using DotNetMQ Client Library, it is very easy and fast to connect and send/receive messages to/from DotNetMQ service. Application – 1 prepares message, sets destination and passes message to DotNetMQ Broker. DotNetMQ brokers will deliver the message to Application – 2 in most efficient and fastest way.

+

What about existing Message Brokers

+

It is clear to see that there is a need for Message Brokers to integrate applications. I searched web, read books to find a free (and open source is prefered if available) Message Broker that is easy to use with .NET. Let’s talk about what I found;

+
    +
  • Apache ActiveMQ (http://activemq.apache.org): It is open source and implemens JMS (Java Message Service is a standart API for messaging in Java world). It has also a .NET client library. I read a complete book “ActiveMQ in Action” to learn and I developed some simple applications. Even though I read the book, I did not see an easy and reliable way to construct an ActiveMQ server graph that work together and routes messages. I also did not see a way to set destination server to a message. It routes messages automatically but I can not control routing efficiently. I understood that it is commonly used with Apache Camel (http://camel.apache.org) to achive common application integration patterns. Apache Camel is also another world to discover and even worse it is just for Java. Finally I think that it is not simple enough to use and especially to configure, monitor and manage it. So, I gave up working on ActiveMQ.
  • +
  • MSMQ (http://msdn.microsoft.com/en-us/library/ms711472(VS.85).aspx): This is a solution of Microsoft and it is most suitable framework to use with .NET applications. It is easy to use and learn, it has tools to monitor queues and messages. Especially it is very suitable for asyncronous communication of applications that are running on same machine or can directly connect to same machine. But I can not find a built-in solution to construct a graph of MSMQ servers that are routing messages. Since routing is my first start point, I eliminated this Broker.
  • +
  • RabbitMQ (http://www.rabbitmq.com): It is developed using Erlang programming platform (that is developed by Ericsson). It is needed to install Erlang first. I spent a lot time to install, configure and write a sample application. It has .NET Client but I got many errors until develop and run a simple application. Especially it was very hard to install and to make work together two rabbitMQ Brokers on two different servers. After a few days I gave up to learn because it must not to be that hard to learn and start to develop some running applications.
  • +
  • OpenAMQ (http://www.openamq.org), ZeroMQ (http://www.zeromq.org): I examined these brokers overally but I see that I can not do easily what I want using .NET.
  • +
  • Others: I also found a few project but they have big missings like routing, persistent messaging, request/reply messaging... etc.
  • +
+

You see that there is no Message Broker that is developed entirely in .NET in the list above.

+

From a user perspective, I just want that I pass “message data, destination server and application name” to my local Broker. I am not interested in the rest. It routes message over network how many times it requires and delivers message to my destination application on the destination server. My messaging system must provide this simplicity for me. This was my first start point and I evaluated Message Borkers according to that point. The figure below shows what I want.

+

Complete Messaging
+ Figure – 4: Automatic Routing messages in a Message Broker servers graph.

+

Application – 1 passes a message to Message Broker in local server (Server – A):

+
    +
  • Destination server: Server – D
  • +
  • Destination application: Application – 2
  • +
  • Message Data: application specific data
  • +
+

Server – A has not direct connection to Server – D. So, Message Brokers forwards the message over servers (message is transmitted through Server – A, Server – B, Server – C and Server – D sequentially) and message finally reaches to Message Broker in Server – D to delivery message to Application – 2. Note that there is another instance of Application – 2 is running on Server – E, but it does not receive this message, since the destination server of the message was Server – D.

+

DotNetMQ provides this functionality and simplicity. It finds the best (shortest) path from source server to destination server on graph and forwards the message.

+

After this comprehensive introduction, let’s see how to use DotNetMQ in practice.

+

Installing And Running DotNetMQ

+

There is no auto install for now but it is very easy to install DotNetMQ. When you download and unzip the download file from top of the article, you will see the Binaries folder. Just copy everything from here to C:\Program Files\DotNetMQ\ and run INSTALL.bat. That's all.

+

You can check windows services to see if DotNetMQ is installed and working.

+

First Application Using DotNetMQ

+

Let’s see DotNetMQ in action. To make first application most simple, I assume that there are two console applications running on same machine (In fact (as we see later in this document) there is not significant difference if applications are in different machines. The only difference is to properly set the name of the destination server in the message).

+

Application1: Gets a string message from user and sends it to Application2.
+ Application2: Writes incoming messages to console screen.

+

Registering Applications to DotNetMQ

+

It is needed to register applications for one time to use with DotNetMQ. It is very simple process. Run DotNetMQ Manager (MDSManager.exe in DotNetMQ program folder (Default C:\Program Files\DotNetMQ\)), open Application List from Applications menu. Click Add New Application button and enter a name for application.

+

Add Application1 and Application2 applications to DotNetMQ as described above. Finally, your application list must be like below.

+

DotNetMQManager
+ Figure – 5: Application List screen of DotNetMQ Manager tool.

+

This screen shows registered applications to DotNetMQ. Connected Clients column shows count of instances of the application that are currently connected to DotNetMQ. It is not needed to restart DotNetMQ because of changes in this screen.

+

Developing Application1

+

Create a new console application with name Application1 in Visual Studio and write the fallowing codes.

+ +
+using System;
+using System.Text;
+using MDS.Client;
+
+namespace Application1
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            //Create MDSClient object to connect to DotNetMQ
+            //Name of this application: Application1
+            var mdsClient = new MDSClient("Application1");
+
+            //Connect to DotNetMQ server
+            mdsClient.Connect();
+
+            Console.WriteLine("Write a text and press enter to send to Application2. Write 'exit' to stop application.");
+
+            while (true)
+            {
+                //Get a message from user
+                var messageText = Console.ReadLine();
+                if (string.IsNullOrEmpty(messageText) || messageText == "exit")
+                {
+                    break;
+                }
+
+                //Create a DotNetMQ Message to send to Application2
+                var message = mdsClient.CreateMessage();
+                //Set destination application name
+                message.DestinationApplicationName = "Application2";
+                //Set message data
+                message.MessageData = Encoding.UTF8.GetBytes(messageText);
+
+                //Send message
+                message.Send();
+            }
+
+            //Disconnect from DotNetMQ server
+            mdsClient.Disconnect();
+        }
+    }
+}
+

While creating a MDSClient object, we pass the application name which connects to DotNetMQ. With this constructor, we connect to DotNetMQ on local server (127.0.0.1) with default port number (10905). Overloaded constructors can be used to connect to another server and port.

+

CreateMessage method of MDSClient returns an object with type IOutgoingMessage. MessageData property is the actual data to send to destination application. It is a byte array. So, we are converting user input text to a byte array using UTF8 encoding. DestinationApplicationName and DestinationServerName properties are used to set destination address of message. If we don’t specify destination server, it is assumed as local server. Finally, we send the message.

+

Developing Application2

+

Create a new console application with name Application2 in Visual Studio and write the fallowing codes.

+ +
+using System;
+using System.Text;
+using MDS.Client;
+
+namespace Application2
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            //Create MDSClient object to connect to DotNetMQ
+            //Name of this application: Application2
+            var mdsClient = new MDSClient("Application2");
+
+            //Register to MessageReceived event to get messages.
+            mdsClient.MessageReceived += MDSClient_MessageReceived;
+
+            //Connect to DotNetMQ server
+            mdsClient.Connect();
+
+            //Wait user to press enter to terminate application
+            Console.WriteLine("Press enter to exit...");
+            Console.ReadLine();
+
+            //Disconnect from DotNetMQ server
+            mdsClient.Disconnect();
+        }
+
+        /// <summary>
+        /// This method handles received messages from other applications via DotNetMQ.
+        /// </summary>
+        /// <param name="sender"></param>
+        /// <param name="e">Message parameters</param>
+        static void MDSClient_MessageReceived(object sender, MessageReceivedEventArgs e)
+        {
+            //Get message
+            var messageText = Encoding.UTF8.GetString(e.Message.MessageData);
+
+            //Process message
+            Console.WriteLine();
+            Console.WriteLine("Text message received : " + messageText);
+            Console.WriteLine("Source application    : " + e.Message.SourceApplicationName);
+
+            //Acknowledge that message is properly handled and processed. So, it will be deleted from queue.
+            e.Message.Acknowledge();
+        }
+    }
+}
+

Creating MDSClient object is similar to Application1 but the application name is Application2. To receive messages for an application, it is needed to register to MessageReceived event of MDSClient. Then we connect to DotNetMQ and stay connected until user presses enter.

+

When a message is sent to Application2, MDSClient_MessageReceived method handles the event. We get the message from the Message property of MessageReceivedEventArgs. Type of message is IIncomingMessage. MessageData property of IIncomingMessage contains the actual message data that is sent by Application1. Since it is a byte array, we are converting it to string using UTF8 encoding. We are writing the message text that is sent by Application1 to console screen.

+

First Apps
+Figure – 6: Application1 sends two messages to Application2 over DotNetMQ.

+

After processing an incoming message, it is needed to Acknowledge the message. That means the message is properly received and correctly processed. So, DotNetMQ removes message from message queue. We can also reject the message using Reject method (if we can not process message on an error case). In this situation, message turns back to the message queue and will be sent later to destination application (or it will be sent to another instance of Application2 on same server if exists). This is a powerfull mechanism of DotNetMQ system. Thus, it is guarantied that the message can not be lost and it is absolutely processed. If you do not acknowledge or reject a message, it is assumed as rejected. So, even if your application crashes, your message is sent back to your application later.

+

Transmit Rule property of Message

+

Before send a message, you can set the Transmit Rule of message like that:

+

message.TransmitRule = MessageTransmitRules.NonPersistent;

+

There are three types of transmit rule:

+
    +
  • StoreAndForward: This is the default transmit rule. Messages are persistent, can not be lost, guaratied to delivery. If Send method does not throw an Exception, then the message is correctly received by DotNetMQ and stored in database. It is stored in database until destination application receives and acknowledges it.
  • +
  • NonPersistent: Messages are not stored in database. It is the fastest way of sending messages. A message is lost only if DotNetMQ server stops.
  • +
  • DirectlySend: This is an exclusive feature of DotNetMQ. This type of messages are direclty sent to application. Sender application is blocked until receiver acknowledges message. So, if sender does not get any exception while calling Send method, message is properly received and acknowledged by receiver application. If an error occurs while transmiting the message, receiver is offline or receiver rejects the message, sender gets an exception on Send method. This rule correctly works even if applications are on different servers (even if there are many servers between applications).
  • +
+

So, since default transmit rule is StoreAndForward, let’s try that:

+
    +
  • Run Application1 (while Application2 is not running), write some messages and close application.
  • +
  • Run Application2, you see that the messages that your messages are received by Application2 and are not lost.
  • +
+

Even if you stop DotNetMQ service from Windows services after sending messages from Application1, your messages don’t be lost. That is the persistence.

+

CommunicationWay property of MDSClient

+

By default, an application can send and receive messages using MDSClient (CommunicationWays.SendAndReceive). If an application doesn’t want to receive messages, it must to set CommunicationWay property to CommunicationWays.Send. This property can be changed before connection or during communication with DotNetMQ.

+

ReConnectServerOnError property of MDSClient

+

By default, MDSClient automatically reconnects to DotNetMQ if it disconnects. So, even if you restart DotNetMQ, it is not needed to restart your applications that are connected to DotNetMQ. You can set ReConnectServerOnError property to false to disable auto reconnect.

+

Configuring DotNetMQ

+

You can configure DotNetMQ in two ways: Using XML settings files or DotNetMQ manager (windows forms application). Here, I will show both of two approaches. Some of configurations require to restart DotNetMQ while others do not.

+

Servers

+

You may run DotNetMQ on only one server. In this situation, there is no need to configure anything for servers. But if you want to run DotNetMQ on more than one server and make them communicate with others, you must define your server graph.

+

A server graph consist of two or more nodes. Each node is a server that has an IP address and TCP port (that is used by DotNetMQ). You can configure/design server graph by using DotNetMQ Manager.

+

Server Graph
+ Figure - 8: DotNetMQ Server Graph managing.

+

In the figure above, you see a server graph that consist of five nodes. The red node represents this server (This server means the server that you are connected with DotNetMQ Manager). A line means that there is a connection (and they can send/receive messages) between two nodes (They are called adjacent nodes). Name of the server/node in graph is important and used when sending messages to the server.

+

You can double-click a server in graph to change it's properties. To connect two server, hold Ctrl, click first one than click the second one (To disconnect, do same thing again). You can set a server as this server by right clicking and selecting Set as this server. You can also delete a server from graph or add a new server by right click menu. Lastly, you can move servers by dragging.

+

After designing your server graph, you must click Save & Update Graph button to save changes. Changes are saved to MDSSettings.xml file in your DotNetMQ installation folder. You must restart DotNetMQ to apply the changes.

+

For the server graph above, the corresponding MDSSettings.xml settings are shown below:

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  <Settings>
+    ...
+  </Settings>
+  <Servers>
+    <Server Name="halil_pc" IpAddress="192.168.10.105" Port="10099" Adjacents="emre_pc" />
+    <Server Name="emre_pc" IpAddress="192.168.10.244" Port="10099" Adjacents="halil_pc,out_server,webserver1,webserver2" />
+    <Server Name="out_server" IpAddress="85.19.100.185" Port="10099" Adjacents="emre_pc" />
+    <Server Name="webserver1" IpAddress="192.168.10.263" Port="10099" Adjacents="emre_pc,webserver2" />
+    <Server Name="webserver2" IpAddress="192.168.10.44" Port="10099" Adjacents="emre_pc,webserver1" />
+  </Servers>
+  <Applications>
+    ...
+  </Applications>
+  <Routes>
+    ...
+  </Routes>
+</MDSConfiguration>
+

Surely, this configuration is made according to your real network. You must install DotNetMQ on all servers in the graph. Also, you must congifure same graph on all servers (You can easily copy server nodes from XML to other servers).

+

DotNetMQ uses short path algorithm to send messages (If no manual route is defined in settings file). Consider an Application A that is running on halil_pc and sending a message to Application B on webserver2. The path is simply: Application A -> halil_pc -> emre_pc -> webserver2 -> Application B. halil_pc knows the next forwarding server (emre_pc) by using the server graph definition.

+

Lastly, MDSSettings.design.xml file contains server design informations (locations of nodes on screen). This is just needed in server graph window in DotNetMQ manager and not needed for runtime of DotNetMQ.

+

Applications

+

As shown Figure - 5, you can add/remove applications those are using DotNetMQ as message broker. It is not needed to restart DotNetMQ for this changes. Application settings are also saved to MDSSettings.xml file as shown below.

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  ...
+  <Applications>
+    <Application Name="Application1" />
+    <Application Name="Application2" />
+  </Applications>
+  ...
+</MDSConfiguration>
+

An application must be in this list to be able to connect to DotNetMQ. If you directly change XML file, you must restart DotNetMQ server.

+

Routing / Load Balancing

+

A usable feature of DotNetMQ is routing. Routing settings (for now) are configured only in xml settings file (MDSSettings.xml). Here, you see two types of routing in the settings file below:

+ +
+<?xml version="1.0" encoding="utf-8" ?>
+<MDSConfiguration>
+  ...
+  <Routes>
+
+    <Route Name="Route-App2" DistributionType="Sequential" >
+      <Filters>
+        <Filter DestinationServer="this" DestinationApplication="Application1" />
+      </Filters>
+      <Destinations>
+        <Destination Server="Server-A" Application="Application1" RouteFactor="1" />
+        <Destination Server="Server-B" Application="Application1" RouteFactor="1" />
+        <Destination Server="Server-C" Application="Application1" RouteFactor="1" />
+    </Destinations>
+    </Route>
+
+    <Route Name="Route-App2" DistributionType="Random" >
+      <Filters>
+        <Filter DestinationServer="this" DestinationApplication="Application2" /> 
+        <Filter SourceApplication="Application2" TransmitRule="StoreAndForward" /> 
+    </Filters>
+      <Destinations>
+        <Destination Server="Server-A" Application="Application2" RouteFactor="1" />
+        <Destination Server="Server-B" Application="Application2" RouteFactor="3" />
+      </Destinations>
+    </Route>
+    
+  </Routes>
+  ...
+</MDSConfiguration>
+

A Route node has two attribute: Name is a user-friendly name of the Route entry (does not effect routing) and DistributionType is strategy of the routing. There are two types of routing strategy:

+
    +
  • Sequential: Messages are routed to destination servers sequentially. RouteFactor of destinations are cosidered while distributing.
  • +
  • Random: Messages are routed to destination servers randomly. Probability of selecting the server A is (RouteFactor(A) / (Total of all RouteFactor values of all destinations in route definition)).
  • +
+

Filters are used to decide to use this route for a message. If properties of a message are suitable for one of the filters, message is routed. There are five conditions (xml attribute) to define a filter:

+
    +
  • SourceServer: First source server of the message. Can be this to indicate this server.
  • +
  • SourceApplication: Sender application of the message.
  • +
  • DestinationServer: Last destination server of the message. Can be this to indicate this server.
  • +
  • DestinationApplication: The application that will receive the message.
  • +
  • TransmitRule: One of these transmit rules: StoreAndForward, DirectlySend or NonPersistent.
  • +
+

If one or more condition is not declared, it is not cosidered while filtering messages. So, if all conditions are empty (or not even declared), all messages are fit to this filter. A filter is selected for a message, only if all conditions are fit to the message. If a message is proper for (at least) one of the filters of a route, the route is selected and used.

+

Destinations are used to route messages to other servers. One of the destinations is selected according to DistributionType property of the Route entry (explained before). A destination must define three attributes:

+
    +
  • Server: Destination server. Can be this to indicate this server.
  • +
  • Application: Destination application. Destination application is generally defined as same as original destination, but you can redirect a message to another application than it's original destination application.
  • +
  • RouteFactor: This property is used to indicate relative selection ratio of a destination. RouteFactor attribute can be used for load balancing. If you want to distribute messages to all servers equally, you can define as 1 for all destinations. But if you have two servers and one of them is more powerfull than other, you can select the first server twice more than the second one by defining appropriate route factors.
  • +
+

You must restart DotNetMQ after changing routes.

+

Other Settings

+

DotNetMQ currently supports three storage type: SQLite (default), MySQL and Memory. You can change storage type in MDSSettings.xml file.

+ +
+<?xml version="1.0" encoding="utf-8"?>
+<MDSConfiguration>
+  ...
+  <Settings>
+    <Setting Key="ThisServerName" Value="halil_pc" />
+    <Setting Key="StorageType" Value="SQLite" />
+  </Settings>
+  ...
+</MDSConfiguration>
+

Storage types must be one of the fallowing values:

+
    +
  • SQLite: Uses SQLite database system. This is default storage type. Uses {DotNetMQ-Install-Directory}\SqliteDB\MDS.s3db file as database.
  • +
  • MySQL-ODBC: Uses MySQL database with ODBC. You must supply MySQLConnectionString setting as connection string (will be explained).
  • +
  • MySQL-Net: Uses MySQL database with .Net Adapter. You must supply MySQLConnectionString setting as connection string (will be explained).
  • +
  • Memory: Uses Memory as storage device. In this case, persistent messages are lost if DotNetMQ is stopped.
  • +
+

Here, there is a sample configuration to use MySQL-ODBC storage type:

+ +
+  <Settings>
+    <Setting Key="ThisServerName" Value="halil_pc" />
+    <Setting Key="StorageType" Value="MySQL-ODBC" />
+    <Setting Key="MySQLConnectionString" Value="uid=root;server=localhost;driver={MySQL ODBC 3.51 Driver};database=mds" />
+  </Settings>
+

You must also create a database named mds in MySQL. You can find two files in Setup folder (in DotNetMQ installation folder) those are needed to create table and stored procedure those are used by DotNetMQ.

+

There is also another setting to define the name of the current/this server (ThisServerName). It must be one of the servers in Servers section. If you use to DotNetMQ manager to edit your servers graph, it is automatically set.

+

Messaging Over Network

+

Sending a message to an application on a remote server is easy as sending a message to an application on current server.

+

A Simple Application

+

Let's consider the network below.

+

Messaging Over Network
+Figure - 8: Messaging of two applications over network with DotNetMQ.

+

There is an application (Application1) is running on ServerA wants to send a message to another application (Application2) on ServerC and there is not direct connection between ServerA and ServerC because of firewall rules. Let's change the applications we developed in First Applications section.

+

There is not even a single change in Application2. Just run Application2 in ServerC and wait incoming messages.

+

There is a minor change in Application1 on sending message. It must set DestinationServerName of message as ServerC.

+ +
+var message = mdsClient.CreateMessage();
+message.DestinationServerName = "ServerC"; //Set destination server name here!
+message.DestinationApplicationName = "Application2";
+message.MessageData = Encoding.UTF8.GetBytes(messageText);
+message.Send();
+

That's all. You do not have to know where the ServerC is, is a direct connection to ServerC... They are all defined in DotNetMQ settings. Note that if you do not set DestinationServerName of a message, it is assumed as current/this server and DotNetMQ sends message to the application on same server. Also, if you define necessary routings, you don't have to set destination server, it is routed by DotNetMQ automatically.

+

Surely, DotNetMQ settings must properly set according to server connections (server graph) and Application1 and Application2 must be registered to DotNetMQ server as described in Configuring DotNetMQ section.

+

A Real Life Case: Distributed SMS Processor.

+

As you see until now, DotNetMQ can be used to build distributed, load balanced application systems. In this section, I'll discuss a real life scenario: A distributed SMS process system.

+

Assume that there is a short message (SMS) service that is used for a polling a singing competition. After all competitors sing their songs, audience send messages like "VOTE 103" to our SMS service to vote their favourite competitor (103 is a sample code to vote a specific competitor). And assume that this polling is made in just 30 minutes and approximately five million people will send SMS to our service.

+

We will receive every message, process it (parse SMS text, update database to increase vote count of competitor) and send a comfirmation message to the sender of the SMS. We must receive messages from two server, process messages on four servers and send comfirmation messages by two servers. We have totally eight servers. Let's see our complete system diagram:

+

SMS System
+ Figure - 9: A Distributed SMS processing system (Click to enlarge).

+

There are three types of applications: Receiver, Processor and Sender. You can use DotNetMQ as message queue and load balancer in such a scenario to build a distributed, scalable message processing system by configuring server graph and routes as described in Configuring DotNetMQ section.

+

Request/Reply Style Messaging with DotNetMQ

+

In most case, an application sends a message to another application and gets a response message. DotNetMQ has built-in support for this type of messaging. Think that a service that is used to query a stock status. Simply there are two types of messages:

+ +
+[Serializable]
+public class StockQueryMessage
+{
+    public string StockCode { get; set; }
+}
+
+[Serializable]
+public class StockQueryResultMessage
+{
+    public string StockCode { get; set; }
+    public int ReservedStockCount { get; set; }
+    public int TotalStockCount { get; set; }
+}
+

A simple Stock server code is shown below.

+ +
+using System;
+using MDS;
+using MDS.Client;
+using StockCommonLib;
+
+namespace StockServer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            var mdsClient = new MDSClient("StockServer");
+            mdsClient.MessageReceived += MDSClient_MessageReceived;
+
+            mdsClient.Connect();
+
+            Console.WriteLine("Press enter to exit...");
+            Console.ReadLine();
+
+            mdsClient.Disconnect();
+        }
+
+        static void MDSClient_MessageReceived(object sender, MessageReceivedEventArgs e)
+        {
+            //Get message
+            var stockQueryMessage = GeneralHelper.DeserializeObject(e.Message.MessageData) as StockQueryMessage;
+            if (stockQueryMessage == null)
+            {
+                return;
+            }
+
+            //Write message content
+            Console.WriteLine("Stock Query Message for: " + stockQueryMessage.StockCode);
+
+            //Get stock counts from a database...
+            int reservedStockCount;
+            int totalStockCount;
+            switch (stockQueryMessage.StockCode)
+            {
+                case "S01":
+                    reservedStockCount = 14;
+                    totalStockCount = 80;
+                    break;
+                case "S02":
+                    reservedStockCount = 0;
+                    totalStockCount = 25;
+                    break;
+                default: //Stock does not exists!
+                    reservedStockCount = -1;
+                    totalStockCount = -1;
+                    break;
+            }
+
+            //Create a reply message for stock query
+            var stockQueryResult = new StockQueryResultMessage
+                                       {
+                                           StockCode = stockQueryMessage.StockCode,
+                                           ReservedStockCount = reservedStockCount,
+                                           TotalStockCount = totalStockCount
+                                       };
+            
+            //Create a MDS response message to send to client
+            var responseMessage = e.Message.CreateResponseMessage();
+            responseMessage.MessageData = GeneralHelper.SerializeObject(stockQueryResult);
+
+            //Send message
+            responseMessage.Send();
+
+            //Acknowledge the original request message. So, it will be deleted from queue.
+            e.Message.Acknowledge();
+        }
+    }
+}
+

Stock server listen incoming StockQueryMessage objects and sends StockQueryResultMessage to the sender. To be simple, I did not selected stocks from a database. A response message is created by CreateResponseMessage() method of incoming message. Lastly, message is acknowledged after response is sent. Now, I will show a simple stock client code to get a stock information from server:

+ +
+using System;
+using MDS;
+using MDS.Client;
+using MDS.Communication.Messages;
+using StockCommonLib;
+
+namespace StockApplication
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Console.WriteLine("Press enter to query a stock status");
+            Console.ReadLine();
+
+            //Connect to DotNetMQ
+            var mdsClient = new MDSClient("StockClient");
+            mdsClient.MessageReceived += mdsClient_MessageReceived;
+            mdsClient.Connect();
+
+            //Create a stock request message
+            var stockQueryMessage = new StockQueryMessage { StockCode = "S01" };
+            
+            //Create a MDS message
+            var requestMessage = mdsClient.CreateMessage();
+            requestMessage.DestinationApplicationName = "StockServer";
+            requestMessage.TransmitRule = MessageTransmitRules.NonPersistent;
+            requestMessage.MessageData = GeneralHelper.SerializeObject(stockQueryMessage);
+
+            //Send message and get response
+            var responseMessage = requestMessage.SendAndGetResponse();
+
+            //Get stock query result message from response message
+            var stockResult = (StockQueryResultMessage) GeneralHelper.DeserializeObject(responseMessage.MessageData);
+
+            //Write stock query result
+            Console.WriteLine("StockCode          = " + stockResult.StockCode);
+            Console.WriteLine("ReservedStockCount = " + stockResult.ReservedStockCount);
+            Console.WriteLine("TotalStockCount    = " + stockResult.TotalStockCount);
+
+            //Acknowledge received message
+            responseMessage.Acknowledge();
+
+            Console.ReadLine();
+
+            //Disconnect from DotNetMQ server.
+            mdsClient.Disconnect();
+        }
+
+        static void mdsClient_MessageReceived(object sender, MessageReceivedEventArgs e)
+        {
+            //Simply acknowledge other received messages
+            e.Message.Acknowledge();
+        }
+    }
+}
+

In the sample above, TransmitRule is selected as NonPersistent to show a sample usage. Surely, you can send StoreAndForward (persistent) messages. Here, a sample screenshot of running applications:

+

Sample Request Reply Messaging
+Figure - 10: Request/Reply style messaging applications.

+

Service-Oriented Architecture On DotNetMQ

+

SOA (Service-Oriented Architecture) is a popular concept for many years. Web services and WCF is two major solutions to SOA. Generally, it is not expected to support SOA from a Message Queue system. Also messaging is an asynchronous and loosely coupled process while a web service method call is typically synchronous and tight coupled. Even (as you see in previous sample applications) messaging is not as easy as calling a remote method. But when your message count increases, your application becomes complicated and harder to maintain.

+

DotNetMQ supports remote method invocation mechanism upon persistent or non-persistent messages. So, you can call a remote method asynchronously that is guarateed to be called!

+

Sample Application: SMS/Mail Sender.

+

Here, we will develop a simple service that can be used to send SMS and e-mail. Maybe it is not needed to write a service for sending an email/sms, all applications can do themselves. But think that you have many applications that are sending emails. What if mail server has a problem while sending an email? Application must try until successfully sent email. So, you must build a queue mechanism in your application to try sending email again and again. At a worse case your application may be a short time running application (such as a web service) or must be closed before sending email. But you have to send the email when mail servers come online and mail must not be lost. In this case, you can develop a seperated mail/sms service that will try sending sms/mail until successfully sent.

+

Service

+

We first developing the mail/sms service. To do this, we must define a class that is delivered from MDSService base class:

+ +
+using System;
+using MDS.Client.MDSServices;
+
+namespace SmsMailServer
+{
+    [MDSService(Description = "This service is a sample mail/sms service.", Version = "1.0.0.0")]
+    public class MyMailSmsService : MDSService
+    {
+        //All parameters and return values can be defined.
+        [MDSServiceMethod(Description = "This method is used send an SMS.")]
+        public void SendSms(
+            [MDSServiceMethodParameter("Phone number to send SMS.")] string phone,
+            [MDSServiceMethodParameter("SMS text to be sent.")] string smsText)
+        {
+            //Process SMS
+            Console.WriteLine("Sending SMS to phone: " + phone);
+            Console.WriteLine("Sms Text: " + smsText);
+
+            //Acknowledge the message
+            IncomingMessage.Acknowledge();
+        }
+
+        //You do not have to define any parameters
+        [MDSServiceMethod]
+        public void SendEmail(string emailAddress, string header, string body)
+        {
+            //Process email
+            Console.WriteLine("Sending an email to " + emailAddress);
+            Console.WriteLine("Header: " + header);
+            Console.WriteLine("Body  : " + body);
+
+            //Acknowledge the message
+            IncomingMessage.Acknowledge();
+        }
+
+        // A simple method just to show return values.
+        [MDSServiceMethod]
+        [return: MDSServiceMethodParameter("True, if phone number is valid.")]
+        public bool IsValidPhone([MDSServiceMethodParameter("Phone number to send SMS.")] string phone)
+        {
+            return (phone.Length == 10);
+        }
+    }
+}
+

As you see, it is just a regular C# class decorated with attributes. Just MDSService and MDSServiceMethod attributes must be defined and all other attributes are optional (but it is good to write them. We will see soon why they are used). Your service methods must has MDSServiceMethod attribute. If you do not want to expose some of your methods, simply do not write MDSServiceMethod attribute.

+

We must also acknowledge the message in the service method. Otherwise, the message (that cause this method call) will not deleted from message queue and our method will be called again. We can also reject the message if we can not process it (for example, if mail server is not working and we can not send emails). If we reject the message, it will be sent us later (this is reliability). You can reach to the original message using IncomingMessage property of MDSService class. Also, you can get remote application (that called the service method) informations using RemoteApplication property.

+

After creating a proper service class, we must create an application to run it. Here, there is a simple console application that runs our MyMailSmsService:

+ +
+using System;
+using MDS.Client.MDSServices;
+
+namespace SmsMailServer
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            using (var service = new MDSServiceApplication("MyMailSmsService"))
+            {
+                service.AddService(new MyMailSmsService());
+                service.Connect();
+
+                Console.WriteLine("Press any key to stop service");
+                Console.ReadLine();
+            }
+        }
+    }
+}
+

As you see, it is just three lines of code to create and run a service. Since MDSService is disposible, you can use a using statement. Also, you can close service manually with Disconnect method of MDSServiceApplication. You can run more than one service on a single MDSServiceApplication using AddService method.

+

Client

+

To Develop an application that uses a DotNetMQ service, you must create a service proxy (like web services and WCF). To do this, you can use MDSServiceProxyGenerator tool. First, compile your service project than run MDSServiceProxyGenerator.exe (in DotNetMQ installation folder).

+

Proxy Generating
+ Figure - 11: Generating proxy class for a service in DotNetMQ.

+

Select your service assembly file (SmsMailServer.exe in this sample project). You can select the service class or generate proxies of all services in given assembly. Enter a namespace and target folder to generate proxy class. After generating proxy class, you can add it to your project.

+

I will not show internals of this proxy class and you also have not to know it (You can see in source codes, it is a very simple class). Your method/parameter attributes are used to generate code comments in tihs proxy file.

+

After adding generated proxy class to our project, we can simple send messages to service just like simple method calls:

+ +
+using System;
+using MDS.Client;
+using MDS.Client.MDSServices;
+using SampleService;
+
+namespace SmsMailClient
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            Console.WriteLine("Press enter to test SendSms method");
+            Console.ReadLine();
+
+            //Application1 is name of an application that sends sms/email.
+            using (var serviceConsumer = new MDSServiceConsumer("Application3"))
+            {
+                //Connect to DotNetMQ server
+                serviceConsumer.Connect();
+
+                //Create service proxy to call remote methods
+                var service = new MyMailSmsServiceProxy(serviceConsumer, new MDSRemoteAppEndPoint("MyMailSmsService"));
+
+                //Call SendSms method
+                service.SendSms("3221234567", "Hello service!");
+            }
+        }
+    }
+}
+

You can also call other methods of the service, get return values as regular method calls. Actually, your method calls are translated to reliable messages. For instance, even if remote application (MyMailSmsService) is not running when SendSms is called, it is called when service become running, so your method calls are also guarantied to be called.

+

You can change transmit rule for messaging using TransmitRule property of the service proxy. If a service method returns void, it's transmit rule is StoreAndForward as default. If a service method returns a value, method call can not be made reliable (since method call is synchronous and waiting a result), it's rule is DirectlySend.

+

You can choice any types as method parameters. If it is a primary type (string, int, byte...) there is no need to additional settings but if you want to use your own classes as method parameter, the class must be marked as Serializable since DotNetMQ uses binary serialization for parameters.

+

Note that you must register MyMailSmsService and Application3 to DotNetMQ before running this sample.

+

Web Services Support

+

Surely, you can connect to DotNetMQ in a web service since it is also a .NET application. But, what if you want to write a ASP.NET web service method to handle messages for an application (and can reply a message in same context). Web services are suitable for such as Request/Reply style method calls.

+

DotNetMQ supports ASP.NET web services and can deliver messages to web services. There is a template web service in DotNetMQ solution to accomplish that. It is defined as below:

+ +
+using System;
+using System.Web.Services;
+using MDS.Client.WebServices;
+
+[WebService(Namespace = "http://www.dotnetmq.com/mds")]
+[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
+public class MDSAppService : WebService
+{
+    /// <summary>
+    /// MDS server sends messages to this method.
+    /// </summary>
+    /// <param name="bytesOfMessage">Byte array form of message</param>
+    /// <returns>Response message to incoming message</returns>
+    [WebMethod(Description = "Receives incoming messages to this web service.")]
+    public byte[] ReceiveMDSMessage(byte[] bytesOfMessage)
+    {
+        var message = WebServiceHelper.DeserializeMessage(bytesOfMessage);
+        try
+        {
+            var response = ProcessMDSMessage(message);
+            return WebServiceHelper.SerializeMessage(response);
+        }
+        catch (Exception ex)
+        {
+            var response = message.CreateResponseMessage();
+            response.Result.Success = false;
+            response.Result.ResultText = "Error in ProcessMDSMessage method: " + ex.Message;
+            return WebServiceHelper.SerializeMessage(response);
+        }
+    }
+
+    /// <summary>
+    /// Processes incoming messages to this web service.
+    /// </summary>
+    /// <param name="message">Message to process</param>
+    /// <returns>Response Message</returns>
+    private IWebServiceResponseMessage ProcessMDSMessage(IWebServiceIncomingMessage message)
+    {
+        //Process message
+
+        //Send response/result
+        var response = message.CreateResponseMessage();
+        response.Result.Success = true;
+        return response;
+    }
+}
+

You do not change ReceiveMDSMessage method and must process the message in ProcessMDSMessage method as shown above. Also you must define address of your web service in MDSSettings.xml as shown below. You can also add web services using DotNetMQ management tool.

+ +
+  ...  
+  <Applications>
+    <Application Name="SampleWebServiceApp">
+      <Communication Type="WebService" Url="http://localhost/SampleWebApplication/SampleService.asmx" />
+    </Application>
+  </Applications>
+  ...  
+

Performance of DotNetMQ

+

There are some test results for messaging in DotNetMQ:

+

Messaging:
+- 10,000 messages in ~25 seconds as persistent (~400 messages/second).
+- +10,000 messages in ~3.5 seconds as non-persistent (~2,850 messages/second).

+

Method Calls (in DotNetMQ Services)
+ - 10,000 method calls in + ~25 seconds as persistent (~400 calls/second).
+ - + 10,000 method calls in +~8.7 seconds as non-persistent (~1,150 calls/second).

+

Test Platform: Intel Core 2 Duo 3,00 GHz CPU. 2 GB RAM PC. Messages/calls are made between two applications running on same computer.

+

History

+
    +
  • 09.05.2011 +
      +
    • First publish.
    • +
    +
  • +
+

References

+

[1] Book: Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions by Gregor Hohpe, Bobby Woolf (Addison Wesley, 2003).

+ + diff --git a/doc/Images/01_MessageQueue.gif b/doc/Images/01_MessageQueue.gif new file mode 100644 index 0000000..5bc1321 Binary files /dev/null and b/doc/Images/01_MessageQueue.gif differ diff --git a/doc/Images/02_BadMessaging.gif b/doc/Images/02_BadMessaging.gif new file mode 100644 index 0000000..ef131db Binary files /dev/null and b/doc/Images/02_BadMessaging.gif differ diff --git a/doc/Images/03_SimpleDotNetMQ.gif b/doc/Images/03_SimpleDotNetMQ.gif new file mode 100644 index 0000000..631a439 Binary files /dev/null and b/doc/Images/03_SimpleDotNetMQ.gif differ diff --git a/doc/Images/04_CompleteMessaging.gif b/doc/Images/04_CompleteMessaging.gif new file mode 100644 index 0000000..c0abcae Binary files /dev/null and b/doc/Images/04_CompleteMessaging.gif differ diff --git a/doc/Images/05_AddApps.jpg b/doc/Images/05_AddApps.jpg new file mode 100644 index 0000000..e6e429c Binary files /dev/null and b/doc/Images/05_AddApps.jpg differ diff --git a/doc/Images/06_FirstApps.gif b/doc/Images/06_FirstApps.gif new file mode 100644 index 0000000..4d65e37 Binary files /dev/null and b/doc/Images/06_FirstApps.gif differ diff --git a/doc/Images/07_ServerGraph1.gif b/doc/Images/07_ServerGraph1.gif new file mode 100644 index 0000000..92d04c1 Binary files /dev/null and b/doc/Images/07_ServerGraph1.gif differ diff --git a/doc/Images/08_NetworkMessaging.gif b/doc/Images/08_NetworkMessaging.gif new file mode 100644 index 0000000..017565f Binary files /dev/null and b/doc/Images/08_NetworkMessaging.gif differ diff --git a/doc/Images/09_DistSmsSystem.gif b/doc/Images/09_DistSmsSystem.gif new file mode 100644 index 0000000..f22f2f8 Binary files /dev/null and b/doc/Images/09_DistSmsSystem.gif differ diff --git a/doc/Images/10_StockService.gif b/doc/Images/10_StockService.gif new file mode 100644 index 0000000..c1fdfb3 Binary files /dev/null and b/doc/Images/10_StockService.gif differ diff --git a/doc/Images/11_ProxyGenerateTool.gif b/doc/Images/11_ProxyGenerateTool.gif new file mode 100644 index 0000000..7582f11 Binary files /dev/null and b/doc/Images/11_ProxyGenerateTool.gif differ diff --git a/doc/Sample.docx b/doc/Sample.docx new file mode 100644 index 0000000..1a1429f Binary files /dev/null and b/doc/Sample.docx differ diff --git a/samples/FirstApplication/Application1/Application1.csproj b/samples/FirstApplication/Application1/Application1.csproj new file mode 100644 index 0000000..d63fe27 --- /dev/null +++ b/samples/FirstApplication/Application1/Application1.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {24D2C6F3-8A26-482A-8B83-DC3A41EFBF85} + Exe + Properties + Application1 + Application1 + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/samples/FirstApplication/Application1/Program.cs b/samples/FirstApplication/Application1/Program.cs new file mode 100644 index 0000000..6a58c5e --- /dev/null +++ b/samples/FirstApplication/Application1/Program.cs @@ -0,0 +1,45 @@ +using System; +using System.Text; +using MDS.Client; + +namespace Application1 +{ + class Program + { + static void Main() + { + //Create MDSClient object to connect to DotNetMQ + //Name of this application: Application1 + var mdsClient = new MDSClient("Application1"); + + //Connect to DotNetMQ server + mdsClient.Connect(); + + Console.WriteLine("Write a text and press enter to send to Application2. Write 'exit' to stop application."); + + while (true) + { + //Get a message from user + var messageText = Console.ReadLine(); + if (string.IsNullOrEmpty(messageText) || messageText == "exit") + { + break; + } + + //Create a DotNetMQ Message to send to Application2 + var message = mdsClient.CreateMessage(); + //Set destination application name + message.DestinationApplicationName = "Application2"; + //message.DestinationServerName = "this_server2"; + //Set message data + message.MessageData = Encoding.UTF8.GetBytes(messageText); + + //Send message + message.Send(); + } + + //Disconnect from DotNetMQ server + mdsClient.Disconnect(); + } + } +} diff --git a/samples/FirstApplication/Application1/Properties/AssemblyInfo.cs b/samples/FirstApplication/Application1/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f562e7a --- /dev/null +++ b/samples/FirstApplication/Application1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Application1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Application1")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3e9e348a-7143-4255-bb18-ce791d90475d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/FirstApplication/Application2/Application2.csproj b/samples/FirstApplication/Application2/Application2.csproj new file mode 100644 index 0000000..9ada20c --- /dev/null +++ b/samples/FirstApplication/Application2/Application2.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {8B275DC8-BF48-4698-A16F-CC1971445D82} + Exe + Properties + Application2 + Application2 + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + \ No newline at end of file diff --git a/samples/FirstApplication/Application2/Program.cs b/samples/FirstApplication/Application2/Program.cs new file mode 100644 index 0000000..dab7c1b --- /dev/null +++ b/samples/FirstApplication/Application2/Program.cs @@ -0,0 +1,48 @@ +using System; +using System.Text; +using MDS.Client; + +namespace Application2 +{ + class Program + { + static void Main(string[] args) + { + //Create MDSClient object to connect to DotNetMQ + //Name of this application: Application2 + var mdsClient = new MDSClient("Application2"); + + //Register to MessageReceived event to get messages. + mdsClient.MessageReceived += MDSClient_MessageReceived; + + //Connect to DotNetMQ server + mdsClient.Connect(); + + //Wait user to press enter to terminate application + Console.WriteLine("Press enter to exit..."); + Console.ReadLine(); + + //Disconnect from DotNetMQ server + mdsClient.Disconnect(); + } + + /// + /// This method handles received messages from other applications via DotNetMQ. + /// + /// + /// Message parameters + static void MDSClient_MessageReceived(object sender, MessageReceivedEventArgs e) + { + //Get message + var messageText = Encoding.UTF8.GetString(e.Message.MessageData); + + //Process message + Console.WriteLine(); + Console.WriteLine("Text message received : " + messageText); + Console.WriteLine("Source application : " + e.Message.SourceApplicationName); + + //Acknowledge that message is properly handled and processed. So, it will be deleted from queue. + e.Message.Acknowledge(); + } + } +} diff --git a/samples/FirstApplication/Application2/Properties/AssemblyInfo.cs b/samples/FirstApplication/Application2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..010d721 --- /dev/null +++ b/samples/FirstApplication/Application2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Application2")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Application2")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e6a686b3-6629-46ba-a018-0dde6af86a07")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/FirstApplication/Dependencies/MDSCommonLib.dll b/samples/FirstApplication/Dependencies/MDSCommonLib.dll new file mode 100644 index 0000000..dac1048 Binary files /dev/null and b/samples/FirstApplication/Dependencies/MDSCommonLib.dll differ diff --git a/samples/FirstApplication/Dependencies/log4net.dll b/samples/FirstApplication/Dependencies/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/samples/FirstApplication/Dependencies/log4net.dll differ diff --git a/samples/FirstApplication/FirstApplication.sln b/samples/FirstApplication/FirstApplication.sln new file mode 100644 index 0000000..3317954 --- /dev/null +++ b/samples/FirstApplication/FirstApplication.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application1", "Application1\Application1.csproj", "{24D2C6F3-8A26-482A-8B83-DC3A41EFBF85}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Application2", "Application2\Application2.csproj", "{8B275DC8-BF48-4698-A16F-CC1971445D82}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{0BBF4910-3B92-4CE8-B910-484EADD3C436}" + ProjectSection(SolutionItems) = preProject + Dependencies\log4net.dll = Dependencies\log4net.dll + Dependencies\MDSCommonLib.dll = Dependencies\MDSCommonLib.dll + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {24D2C6F3-8A26-482A-8B83-DC3A41EFBF85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24D2C6F3-8A26-482A-8B83-DC3A41EFBF85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24D2C6F3-8A26-482A-8B83-DC3A41EFBF85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24D2C6F3-8A26-482A-8B83-DC3A41EFBF85}.Release|Any CPU.Build.0 = Release|Any CPU + {8B275DC8-BF48-4698-A16F-CC1971445D82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B275DC8-BF48-4698-A16F-CC1971445D82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B275DC8-BF48-4698-A16F-CC1971445D82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B275DC8-BF48-4698-A16F-CC1971445D82}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/SmsMailServerSystem/Dependencies/MDSCommonLib.dll b/samples/SmsMailServerSystem/Dependencies/MDSCommonLib.dll new file mode 100644 index 0000000..dac1048 Binary files /dev/null and b/samples/SmsMailServerSystem/Dependencies/MDSCommonLib.dll differ diff --git a/samples/SmsMailServerSystem/Dependencies/log4net.dll b/samples/SmsMailServerSystem/Dependencies/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/samples/SmsMailServerSystem/Dependencies/log4net.dll differ diff --git a/samples/SmsMailServerSystem/SmsMailClient/MyMailSmsServiceProxy.cs b/samples/SmsMailServerSystem/SmsMailClient/MyMailSmsServiceProxy.cs new file mode 100644 index 0000000..8782c63 --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailClient/MyMailSmsServiceProxy.cs @@ -0,0 +1,92 @@ +/* This code file is generated by MDSService Proxy Generator tool. + * + * Service Name : MyMailSmsService + * Service version : 1.0.0.0 + * Generating date : 2011-04-04 22:07:02 + */ + +using System; +using MDS.Client; +using MDS.Client.MDSServices; + +namespace SampleService +{ + /// + /// This class is a proxy class to use MyMailSmsService service. + /// Service Description: This service is a sample mail/sms service. + /// + public partial class MyMailSmsServiceProxy : MDSServiceProxyBase + { + #region Constructor + + /// + /// Creates a new instance of MyMailSmsServiceProxy. + /// + /// Reference to a MDSServiceConsumer object to send/receive MDS messages + /// Remote application end point to send requests + public MyMailSmsServiceProxy(MDSServiceConsumer serviceConsumer, MDSRemoteAppEndPoint remoteEndPoint) + : base(serviceConsumer, remoteEndPoint, "MyMailSmsService") + { + + } + + #endregion + + #region MyMailSmsService methods + + /// + /// This method is used send an SMS. + /// + /// Phone number to send SMS. + /// SMS text to be sent. + public void SendSms(string phone, string smsText) + { + InvokeRemoteMethod("SendSms", phone, smsText); + } + + /// + /// No method summary available. + /// + public void SendEmail(string emailAddress, string header, string body) + { + InvokeRemoteMethod("SendEmail", emailAddress, header, body); + } + + /// + /// No method summary available. + /// + /// Phone number to send SMS. + /// True, if phone number is valid. + public bool IsValidPhone(string phone) + { + return (bool) InvokeRemoteMethodAndGetResult("IsValidPhone", phone); + } + + #endregion + + #region Default (predefined) service methods + + /// + /// This method generates client proxy class to use this service. Clients can update it's proxy classes via calling this method remotely. + /// + /// Namespace of generating proxy class + /// Proxy class code to use this service + public string GenerateProxyClass(string namespaceName) + { + return (string) InvokeRemoteMethodAndGetResult("GenerateProxyClass", namespaceName); + } + + /// + /// This method can be used to check if service is available. + /// + /// A message to reply + /// Reply to message as formatted: 'RE: message' + public string CheckServiceIsAvailable(string message) + { + return (string) InvokeRemoteMethodAndGetResult("CheckServiceIsAvailable", message); + } + + #endregion + } +} + diff --git a/samples/SmsMailServerSystem/SmsMailClient/Program.cs b/samples/SmsMailServerSystem/SmsMailClient/Program.cs new file mode 100644 index 0000000..d8b3853 --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailClient/Program.cs @@ -0,0 +1,29 @@ +using System; +using MDS.Client; +using MDS.Client.MDSServices; +using SampleService; + +namespace SmsMailClient +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Press enter to test SendSms method"); + Console.ReadLine(); + + //Application1 is name of an application that sends sms/email. + using (var serviceConsumer = new MDSServiceConsumer("Application3")) + { + //Connect to DotNetMQ server + serviceConsumer.Connect(); + + //Create service proxy to call remote methods + var service = new MyMailSmsServiceProxy(serviceConsumer, new MDSRemoteAppEndPoint("MyMailSmsService")); + + //Call SendSms method + service.SendSms("3221234567", "Hello service!"); + } + } + } +} diff --git a/samples/SmsMailServerSystem/SmsMailClient/Properties/AssemblyInfo.cs b/samples/SmsMailServerSystem/SmsMailClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..62a116d --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SmsMailClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SmsMailClient")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8ec897f4-6944-48d5-858e-83695fd30e5f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/SmsMailServerSystem/SmsMailClient/SmsMailClient.csproj b/samples/SmsMailServerSystem/SmsMailClient/SmsMailClient.csproj new file mode 100644 index 0000000..0ffb50b --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailClient/SmsMailClient.csproj @@ -0,0 +1,68 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D1129E03-72D6-449F-BF95-A489BBCFA090} + Exe + Properties + SmsMailClient + SmsMailClient + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SmsMailServerSystem/SmsMailServer/MyMailSmsService.cs b/samples/SmsMailServerSystem/SmsMailServer/MyMailSmsService.cs new file mode 100644 index 0000000..d2943cb --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailServer/MyMailSmsService.cs @@ -0,0 +1,48 @@ +using System; +using MDS.Client.MDSServices; + +namespace SmsMailServer +{ + [MDSService(Description = "This service is a sample mail/sms service.", Version = "1.0.0.0")] + public class MyMailSmsService : MDSService + { + //All parameters and return values can be defined. + [MDSServiceMethod(Description = "This method is used send an SMS.")] + public void SendSms( + [MDSServiceMethodParameter("Phone number to send SMS.")] string phone, + [MDSServiceMethodParameter("SMS text to be sent.")]string smsText) + { + //Process SMS + Console.WriteLine("Sending SMS to phone: " + phone); + Console.WriteLine("Sms Text: " + smsText); + + //Acknowledge the message + IncomingMessage.Acknowledge(); + } + + //You do not have to define any parameters + [MDSServiceMethod] + public void SendEmail(string emailAddress, string header, string body) + { + //Process email + Console.WriteLine("Sending an email to " + emailAddress); + Console.WriteLine("Header: " + header); + Console.WriteLine("Body : " + body); + + //Acknowledge the message + IncomingMessage.Acknowledge(); + } + + // A simple method just to show return values. + [MDSServiceMethod] + [return: MDSServiceMethodParameter("True, if phone number is valid.")] + public bool IsValidPhone([MDSServiceMethodParameter("Phone number to send SMS.")] string phone) + { + //Acknowledge the message + IncomingMessage.Acknowledge(); + + //Return result + return (phone.Length == 10); + } + } +} \ No newline at end of file diff --git a/samples/SmsMailServerSystem/SmsMailServer/Program.cs b/samples/SmsMailServerSystem/SmsMailServer/Program.cs new file mode 100644 index 0000000..e6d255f --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailServer/Program.cs @@ -0,0 +1,20 @@ +using System; +using MDS.Client.MDSServices; + +namespace SmsMailServer +{ + class Program + { + static void Main(string[] args) + { + using (var service = new MDSServiceApplication("MyMailSmsService")) + { + service.AddService(new MyMailSmsService()); + service.Connect(); + + Console.WriteLine("Press any key to stop service"); + Console.ReadLine(); + } + } + } +} diff --git a/samples/SmsMailServerSystem/SmsMailServer/Properties/AssemblyInfo.cs b/samples/SmsMailServerSystem/SmsMailServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3216119 --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SmsMailServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("SmsMailServer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1083d8f3-35bf-4348-a593-d9617e88fd2f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/SmsMailServerSystem/SmsMailServer/SmsMailServer.csproj b/samples/SmsMailServerSystem/SmsMailServer/SmsMailServer.csproj new file mode 100644 index 0000000..b4bc04f --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailServer/SmsMailServer.csproj @@ -0,0 +1,68 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {43C9DB3E-7370-4CC9-866A-D58636936550} + Exe + Properties + SmsMailServer + SmsMailServer + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/SmsMailServerSystem/SmsMailServerSystem.sln b/samples/SmsMailServerSystem/SmsMailServerSystem.sln new file mode 100644 index 0000000..ee17ce1 --- /dev/null +++ b/samples/SmsMailServerSystem/SmsMailServerSystem.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmsMailServer", "SmsMailServer\SmsMailServer.csproj", "{43C9DB3E-7370-4CC9-866A-D58636936550}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SmsMailClient", "SmsMailClient\SmsMailClient.csproj", "{D1129E03-72D6-449F-BF95-A489BBCFA090}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{C8B7D2B3-5F0D-40A7-9C0D-090B6FBF6193}" + ProjectSection(SolutionItems) = preProject + Dependencies\log4net.dll = Dependencies\log4net.dll + Dependencies\MDSCommonLib.dll = Dependencies\MDSCommonLib.dll + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {43C9DB3E-7370-4CC9-866A-D58636936550}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43C9DB3E-7370-4CC9-866A-D58636936550}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43C9DB3E-7370-4CC9-866A-D58636936550}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43C9DB3E-7370-4CC9-866A-D58636936550}.Release|Any CPU.Build.0 = Release|Any CPU + {D1129E03-72D6-449F-BF95-A489BBCFA090}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1129E03-72D6-449F-BF95-A489BBCFA090}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1129E03-72D6-449F-BF95-A489BBCFA090}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1129E03-72D6-449F-BF95-A489BBCFA090}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/StockApplication/Dependencies/MDSCommonLib.dll b/samples/StockApplication/Dependencies/MDSCommonLib.dll new file mode 100644 index 0000000..dac1048 Binary files /dev/null and b/samples/StockApplication/Dependencies/MDSCommonLib.dll differ diff --git a/samples/StockApplication/Dependencies/log4net.dll b/samples/StockApplication/Dependencies/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/samples/StockApplication/Dependencies/log4net.dll differ diff --git a/samples/StockApplication/StockApplication.sln b/samples/StockApplication/StockApplication.sln new file mode 100644 index 0000000..74b58e2 --- /dev/null +++ b/samples/StockApplication/StockApplication.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StockApplication", "StockApplication\StockApplication.csproj", "{FFBE0873-B0E2-43D2-89B1-A27179FE5054}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StockServer", "StockServer\StockServer.csproj", "{5CB63ED1-5B3B-4EDA-8148-6838329EF152}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StockCommonLib", "StockCommonLib\StockCommonLib.csproj", "{050CB1CF-3528-4D61-A9F8-5359A9B72BF3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{439FC8B6-B48E-437D-A5A1-919C39B85C2B}" + ProjectSection(SolutionItems) = preProject + Dependencies\log4net.dll = Dependencies\log4net.dll + Dependencies\MDSCommonLib.dll = Dependencies\MDSCommonLib.dll + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFBE0873-B0E2-43D2-89B1-A27179FE5054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFBE0873-B0E2-43D2-89B1-A27179FE5054}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFBE0873-B0E2-43D2-89B1-A27179FE5054}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFBE0873-B0E2-43D2-89B1-A27179FE5054}.Release|Any CPU.Build.0 = Release|Any CPU + {5CB63ED1-5B3B-4EDA-8148-6838329EF152}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CB63ED1-5B3B-4EDA-8148-6838329EF152}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CB63ED1-5B3B-4EDA-8148-6838329EF152}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CB63ED1-5B3B-4EDA-8148-6838329EF152}.Release|Any CPU.Build.0 = Release|Any CPU + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/samples/StockApplication/StockApplication/Program.cs b/samples/StockApplication/StockApplication/Program.cs new file mode 100644 index 0000000..66fc0b8 --- /dev/null +++ b/samples/StockApplication/StockApplication/Program.cs @@ -0,0 +1,56 @@ +using System; +using MDS; +using MDS.Client; +using MDS.Communication.Messages; +using StockCommonLib; + +namespace StockApplication +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Press enter to query a stock status"); + Console.ReadLine(); + + //Connect to DotNetMQ + var mdsClient = new MDSClient("StockClient"); + mdsClient.MessageReceived += mdsClient_MessageReceived; + mdsClient.Connect(); + + //Create a stock request message + var stockQueryMessage = new StockQueryMessage { StockCode = "S01" }; + + //Create a MDS message + var requestMessage = mdsClient.CreateMessage(); + requestMessage.DestinationApplicationName = "StockServer"; + requestMessage.TransmitRule = MessageTransmitRules.NonPersistent; + requestMessage.MessageData = GeneralHelper.SerializeObject(stockQueryMessage); + + //Send message and get response + var responseMessage = requestMessage.SendAndGetResponse(); + + //Get stock query result message from response message + var stockResult = (StockQueryResultMessage) GeneralHelper.DeserializeObject(responseMessage.MessageData); + + //Write stock query result + Console.WriteLine("StockCode = " + stockResult.StockCode); + Console.WriteLine("ReservedStockCount = " + stockResult.ReservedStockCount); + Console.WriteLine("TotalStockCount = " + stockResult.TotalStockCount); + + //Acknowledge received message + responseMessage.Acknowledge(); + + Console.ReadLine(); + + //Disconnect from DotNetMQ server. + mdsClient.Disconnect(); + } + + static void mdsClient_MessageReceived(object sender, MessageReceivedEventArgs e) + { + //Simply acknowledge other received messages + e.Message.Acknowledge(); + } + } +} diff --git a/samples/StockApplication/StockApplication/Properties/AssemblyInfo.cs b/samples/StockApplication/StockApplication/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..43053f6 --- /dev/null +++ b/samples/StockApplication/StockApplication/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StockApplication")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("StockApplication")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7d0c9a83-dc18-4f53-8d51-2ed3d75e6c3a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/StockApplication/StockApplication/StockApplication.csproj b/samples/StockApplication/StockApplication/StockApplication.csproj new file mode 100644 index 0000000..5b0f89e --- /dev/null +++ b/samples/StockApplication/StockApplication/StockApplication.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {FFBE0873-B0E2-43D2-89B1-A27179FE5054} + Exe + Properties + StockApplication + StockApplication + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3} + StockCommonLib + + + + + \ No newline at end of file diff --git a/samples/StockApplication/StockCommonLib/Properties/AssemblyInfo.cs b/samples/StockApplication/StockCommonLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..41b18bb --- /dev/null +++ b/samples/StockApplication/StockCommonLib/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StockCommonLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("StockCommonLib")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("fe64a4a4-1f89-410c-a19b-e340553d6e97")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/StockApplication/StockCommonLib/StockCommonLib.csproj b/samples/StockApplication/StockCommonLib/StockCommonLib.csproj new file mode 100644 index 0000000..330df0d --- /dev/null +++ b/samples/StockApplication/StockCommonLib/StockCommonLib.csproj @@ -0,0 +1,60 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3} + Library + Properties + StockCommonLib + StockCommonLib + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/StockApplication/StockCommonLib/StockQueryMessage.cs b/samples/StockApplication/StockCommonLib/StockQueryMessage.cs new file mode 100644 index 0000000..ed18ad8 --- /dev/null +++ b/samples/StockApplication/StockCommonLib/StockQueryMessage.cs @@ -0,0 +1,10 @@ +using System; + +namespace StockCommonLib +{ + [Serializable] + public class StockQueryMessage + { + public string StockCode { get; set; } + } +} diff --git a/samples/StockApplication/StockCommonLib/StockQueryResultMessage.cs b/samples/StockApplication/StockCommonLib/StockQueryResultMessage.cs new file mode 100644 index 0000000..47e5704 --- /dev/null +++ b/samples/StockApplication/StockCommonLib/StockQueryResultMessage.cs @@ -0,0 +1,12 @@ +using System; + +namespace StockCommonLib +{ + [Serializable] + public class StockQueryResultMessage + { + public string StockCode { get; set; } + public int ReservedStockCount { get; set; } + public int TotalStockCount { get; set; } + } +} diff --git a/samples/StockApplication/StockServer/Program.cs b/samples/StockApplication/StockServer/Program.cs new file mode 100644 index 0000000..cbd6008 --- /dev/null +++ b/samples/StockApplication/StockServer/Program.cs @@ -0,0 +1,73 @@ +using System; +using MDS; +using MDS.Client; +using StockCommonLib; + +namespace StockServer +{ + class Program + { + static void Main(string[] args) + { + var mdsClient = new MDSClient("StockServer"); + mdsClient.MessageReceived += MDSClient_MessageReceived; + + mdsClient.Connect(); + + Console.WriteLine("Press enter to exit..."); + Console.ReadLine(); + + mdsClient.Disconnect(); + } + + static void MDSClient_MessageReceived(object sender, MessageReceivedEventArgs e) + { + //Get message + var stockQueryMessage = GeneralHelper.DeserializeObject(e.Message.MessageData) as StockQueryMessage; + if (stockQueryMessage == null) + { + return; + } + + //Write message content + Console.WriteLine("Stock Query Message for: " + stockQueryMessage.StockCode); + + //Get stock counts from a database... + int reservedStockCount; + int totalStockCount; + switch (stockQueryMessage.StockCode) + { + case "S01": + reservedStockCount = 14; + totalStockCount = 80; + break; + case "S02": + reservedStockCount = 0; + totalStockCount = 25; + break; + default: + reservedStockCount = -1; + totalStockCount = -1; + break; + } + + //Create a reply message for stock query + var stockQueryResult = new StockQueryResultMessage + { + StockCode = stockQueryMessage.StockCode, + ReservedStockCount = reservedStockCount, + TotalStockCount = totalStockCount + }; + + //Create a MDS response message to send to client + var responseMessage = e.Message.CreateResponseMessage(); + responseMessage.MessageData = GeneralHelper.SerializeObject(stockQueryResult); + + //Send message + responseMessage.Send(); + + //Acknowledge the original request message. So, it will be deleted from queue. + e.Message.Acknowledge(); + } + } +} diff --git a/samples/StockApplication/StockServer/Properties/AssemblyInfo.cs b/samples/StockApplication/StockServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..77c2e09 --- /dev/null +++ b/samples/StockApplication/StockServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StockServer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("StockServer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0646182c-9082-4af4-a67f-277bef09467c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/samples/StockApplication/StockServer/StockServer.csproj b/samples/StockApplication/StockServer/StockServer.csproj new file mode 100644 index 0000000..e22983b --- /dev/null +++ b/samples/StockApplication/StockServer/StockServer.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {5CB63ED1-5B3B-4EDA-8148-6838329EF152} + Exe + Properties + StockServer + StockServer + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\log4net.dll + + + False + ..\Dependencies\MDSCommonLib.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {050CB1CF-3528-4D61-A9F8-5359A9B72BF3} + StockCommonLib + + + + + \ No newline at end of file diff --git a/samples/WebServiceSample/App_Code/MDSAppService.cs b/samples/WebServiceSample/App_Code/MDSAppService.cs new file mode 100644 index 0000000..5939f41 --- /dev/null +++ b/samples/WebServiceSample/App_Code/MDSAppService.cs @@ -0,0 +1,47 @@ +using System; +using System.Web.Services; +using MDS.Client.WebServices; + +[WebService(Namespace = "http://www.dotnetmq.com/mds")] +[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] +public class MDSAppService : WebService +{ + /// + /// MDS server sends messages to this method. + /// + /// Byte array form of message + /// Response message to incoming message + [WebMethod(Description = "Receives incoming messages to this web service.")] + public byte[] ReceiveMDSMessage(byte[] bytesOfMessage) + { + var message = WebServiceHelper.DeserializeMessage(bytesOfMessage); + try + { + var response = ProcessMDSMessage(message); + return WebServiceHelper.SerializeMessage(response); + } + catch (Exception ex) + { + var response = message.CreateResponseMessage(); + response.Result.Success = false; + response.Result.ResultText = "Error in ProcessMDSMessage method: " + ex.Message; + return WebServiceHelper.SerializeMessage(response); + } + } + + /// + /// Processes incoming messages to this web service. + /// + /// Message to process + /// Response Message + private IWebServiceResponseMessage ProcessMDSMessage(IWebServiceIncomingMessage message) + { + //Process message + + //Send response/result + var response = message.CreateResponseMessage(); + response.Result.Success = true; + return response; + } +} + diff --git a/samples/WebServiceSample/MDSAppService.asmx b/samples/WebServiceSample/MDSAppService.asmx new file mode 100644 index 0000000..35d0df1 --- /dev/null +++ b/samples/WebServiceSample/MDSAppService.asmx @@ -0,0 +1 @@ +<%@ WebService Language="C#" CodeBehind="~/App_Code/MDSAppService.cs" Class="MDSAppService" %> diff --git a/samples/WebServiceSample/web.config b/samples/WebServiceSample/web.config new file mode 100644 index 0000000..981f1b9 --- /dev/null +++ b/samples/WebServiceSample/web.config @@ -0,0 +1,120 @@ + + + + + + +
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Dependencies/Databases/MSSQL/DB-FILES.zip b/src/Dependencies/Databases/MSSQL/DB-FILES.zip new file mode 100644 index 0000000..a40dafa Binary files /dev/null and b/src/Dependencies/Databases/MSSQL/DB-FILES.zip differ diff --git a/src/Dependencies/Databases/MSSQL/ReadMe.txt b/src/Dependencies/Databases/MSSQL/ReadMe.txt new file mode 100644 index 0000000..3d26e28 --- /dev/null +++ b/src/Dependencies/Databases/MSSQL/ReadMe.txt @@ -0,0 +1,6 @@ +To create MS SQL Server database for DotNetMQ, fallow one of two option below: + + First option : Create a database in SQL Server named mds and run query in Tables.txt file. + Second option : Unzip DB-FILES.zip and attach to SQL Server. + +After creating database, you must configure MDSSettings.xml to use MS SQL Server as DB engine in DotNetMQ. \ No newline at end of file diff --git a/src/Dependencies/Databases/MSSQL/Tables.txt b/src/Dependencies/Databases/MSSQL/Tables.txt new file mode 100644 index 0000000..1e72ccd --- /dev/null +++ b/src/Dependencies/Databases/MSSQL/Tables.txt @@ -0,0 +1,36 @@ +USE [mds] +GO + +/****** Object: Table [dbo].[Messages] Script Date: 05/22/2011 22:18:28 ******/ +SET ANSI_NULLS ON +GO + +SET QUOTED_IDENTIFIER ON +GO + +SET ANSI_PADDING ON +GO + +CREATE TABLE [dbo].[Messages]( + [Id] [int] IDENTITY(1,1) NOT NULL, + [MessageId] [nvarchar](100) NOT NULL, + [DestServer] [nvarchar](100) NOT NULL, + [NextServer] [nvarchar](100) NOT NULL, + [DestApplication] [nvarchar](100) NOT NULL, + [MessageData] [varbinary](max) NOT NULL, + [MessageDataLength] [int] NOT NULL, + [RecordDate] [datetime] NOT NULL, + CONSTRAINT [PK_messages] PRIMARY KEY CLUSTERED +( + [Id] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] +) ON [PRIMARY] + +GO + +SET ANSI_PADDING OFF +GO + +ALTER TABLE [dbo].[Messages] ADD CONSTRAINT [DF_messages_RecordDate] DEFAULT (getdate()) FOR [RecordDate] +GO + diff --git a/src/Dependencies/Databases/MySQL/ReadMe.txt b/src/Dependencies/Databases/MySQL/ReadMe.txt new file mode 100644 index 0000000..49028f8 --- /dev/null +++ b/src/Dependencies/Databases/MySQL/ReadMe.txt @@ -0,0 +1,2 @@ +To create MySQL database for DotNetMQ, create a database in MySQL Server named 'mds' and run query in Tables.txt file. +After creating database, you must configure MDSSettings.xml to use MySQL Server as DB engine in DotNetMQ. \ No newline at end of file diff --git a/src/Dependencies/Databases/MySQL/Tables.txt b/src/Dependencies/Databases/MySQL/Tables.txt new file mode 100644 index 0000000..d366dab --- /dev/null +++ b/src/Dependencies/Databases/MySQL/Tables.txt @@ -0,0 +1,13 @@ +CREATE TABLE `mds`.`messages` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `MessageId` varchar(100) NOT NULL, + `DestServer` varchar(100) NOT NULL, + `NextServer` varchar(100) NOT NULL, + `DestApplication` varchar(100) NOT NULL, + `MessageData` blob NOT NULL, + `MessageDataLength` int(10) unsigned NOT NULL, + `RecordDate` datetime NOT NULL, + PRIMARY KEY (`Id`), + KEY `IX_Ser_App` (`NextServer`,`DestApplication`,`Id`) USING BTREE, + KEY `IX_Ser` (`NextServer`,`Id`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=latin5; \ No newline at end of file diff --git a/src/Dependencies/Databases/SQLite/MDS.s3db b/src/Dependencies/Databases/SQLite/MDS.s3db new file mode 100644 index 0000000..1a1cdce Binary files /dev/null and b/src/Dependencies/Databases/SQLite/MDS.s3db differ diff --git a/src/Dependencies/Databases/SQLite/ReadMe.txt b/src/Dependencies/Databases/SQLite/ReadMe.txt new file mode 100644 index 0000000..b8a9cbe --- /dev/null +++ b/src/Dependencies/Databases/SQLite/ReadMe.txt @@ -0,0 +1 @@ +MDS.s3db is an empty SQLite database file to be used in DotNetMQ. \ No newline at end of file diff --git a/src/Dependencies/Libraries/MySql.Data.dll b/src/Dependencies/Libraries/MySql.Data.dll new file mode 100644 index 0000000..7aa95ec Binary files /dev/null and b/src/Dependencies/Libraries/MySql.Data.dll differ diff --git a/src/Dependencies/Libraries/System.Data.SQLite.dll b/src/Dependencies/Libraries/System.Data.SQLite.dll new file mode 100644 index 0000000..aa398bb Binary files /dev/null and b/src/Dependencies/Libraries/System.Data.SQLite.dll differ diff --git a/src/Dependencies/Libraries/log4net.dll b/src/Dependencies/Libraries/log4net.dll new file mode 100644 index 0000000..ffc57e1 Binary files /dev/null and b/src/Dependencies/Libraries/log4net.dll differ diff --git a/src/Dependencies/Libraries/log4net.xml b/src/Dependencies/Libraries/log4net.xml new file mode 100644 index 0000000..db9e99f --- /dev/null +++ b/src/Dependencies/Libraries/log4net.xml @@ -0,0 +1,28655 @@ + + + + log4net + + + + + Appender that logs to a database. + + + + appends logging events to a table within a + database. The appender can be configured to specify the connection + string by setting the property. + The connection type (provider) can be specified by setting the + property. For more information on database connection strings for + your specific database see http://www.connectionstrings.com/. + + + Records are written into the database either using a prepared + statement or a stored procedure. The property + is set to (System.Data.CommandType.Text) to specify a prepared statement + or to (System.Data.CommandType.StoredProcedure) to specify a stored + procedure. + + + The prepared statement text or the name of the stored procedure + must be set in the property. + + + The prepared statement or stored procedure can take a number + of parameters. Parameters are added using the + method. This adds a single to the + ordered list of parameters. The + type may be subclassed if required to provide database specific + functionality. The specifies + the parameter name, database type, size, and how the value should + be generated using a . + + + + An example of a SQL Server table that could be logged to: + + CREATE TABLE [dbo].[Log] ( + [ID] [int] IDENTITY (1, 1) NOT NULL , + [Date] [datetime] NOT NULL , + [Thread] [varchar] (255) NOT NULL , + [Level] [varchar] (20) NOT NULL , + [Logger] [varchar] (255) NOT NULL , + [Message] [varchar] (4000) NOT NULL + ) ON [PRIMARY] + + + + An example configuration to log to the above table: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Julian Biddle + Nicko Cadell + Gert Driesen + Lance Nehring + + + + Abstract base class implementation of that + buffers events in a fixed size buffer. + + + + This base class should be used by appenders that need to buffer a + number of events before logging them. For example the + buffers events and then submits the entire contents of the buffer to + the underlying database in one go. + + + Subclasses should override the + method to deliver the buffered events. + + The BufferingAppenderSkeleton maintains a fixed size cyclic + buffer of events. The size of the buffer is set using + the property. + + A is used to inspect + each event as it arrives in the appender. If the + triggers, then the current buffer is sent immediately + (see ). Otherwise the event + is stored in the buffer. For example, an evaluator can be used to + deliver the events immediately when an ERROR event arrives. + + + The buffering appender can be configured in a mode. + By default the appender is NOT lossy. When the buffer is full all + the buffered events are sent with . + If the property is set to true then the + buffer will not be sent when it is full, and new events arriving + in the appender will overwrite the oldest event in the buffer. + In lossy mode the buffer will only be sent when the + triggers. This can be useful behavior when you need to know about + ERROR events but not about events with a lower level, configure an + evaluator that will trigger when an ERROR event arrives, the whole + buffer will be sent which gives a history of events leading up to + the ERROR event. + + + Nicko Cadell + Gert Driesen + + + + Abstract base class implementation of . + + + + This class provides the code for common functionality, such + as support for threshold filtering and support for general filters. + + + Appenders can also implement the interface. Therefore + they would require that the method + be called after the appenders properties have been configured. + + + Nicko Cadell + Gert Driesen + + + + Implement this interface for your own strategies for printing log statements. + + + + Implementors should consider extending the + class which provides a default implementation of this interface. + + + Appenders can also implement the interface. Therefore + they would require that the method + be called after the appenders properties have been configured. + + + Nicko Cadell + Gert Driesen + + + + Closes the appender and releases resources. + + + + Releases any resources allocated within the appender such as file handles, + network connections, etc. + + + It is a programming error to append to a closed appender. + + + + + + Log the logging event in Appender specific way. + + The event to log + + + This method is called to log a message into this appender. + + + + + + Gets or sets the name of this appender. + + The name of the appender. + + The name uniquely identifies the appender. + + + + + Interface for appenders that support bulk logging. + + + + This interface extends the interface to + support bulk logging of objects. Appenders + should only implement this interface if they can bulk log efficiently. + + + Nicko Cadell + + + + Log the array of logging events in Appender specific way. + + The events to log + + + This method is called to log an array of events into this appender. + + + + + + Interface used to delay activate a configured object. + + + + This allows an object to defer activation of its options until all + options have been set. This is required for components which have + related options that remain ambiguous until all are set. + + + If a component implements this interface then the method + must be called by the container after its all the configured properties have been set + and before the component can be used. + + + Nicko Cadell + + + + Activate the options that were previously set with calls to properties. + + + + This allows an object to defer activation of its options until all + options have been set. This is required for components which have + related options that remain ambiguous until all are set. + + + If a component implements this interface then this method must be called + after its properties have been set before the component can be used. + + + + + + Initial buffer size + + + + + Maximum buffer size before it is recycled + + + + + Default constructor + + + Empty default constructor + + + + + Finalizes this appender by calling the implementation's + method. + + + + If this appender has not been closed then the Finalize method + will call . + + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Closes the appender and release resources. + + + + Release any resources allocated within the appender such as file handles, + network connections, etc. + + + It is a programming error to append to a closed appender. + + + This method cannot be overridden by subclasses. This method + delegates the closing of the appender to the + method which must be overridden in the subclass. + + + + + + Performs threshold checks and invokes filters before + delegating actual logging to the subclasses specific + method. + + The event to log. + + + This method cannot be overridden by derived classes. A + derived class should override the method + which is called by this method. + + + The implementation of this method is as follows: + + + + + + Checks that the severity of the + is greater than or equal to the of this + appender. + + + + Checks that the chain accepts the + . + + + + + Calls and checks that + it returns true. + + + + + If all of the above steps succeed then the + will be passed to the abstract method. + + + + + + Performs threshold checks and invokes filters before + delegating actual logging to the subclasses specific + method. + + The array of events to log. + + + This method cannot be overridden by derived classes. A + derived class should override the method + which is called by this method. + + + The implementation of this method is as follows: + + + + + + Checks that the severity of the + is greater than or equal to the of this + appender. + + + + Checks that the chain accepts the + . + + + + + Calls and checks that + it returns true. + + + + + If all of the above steps succeed then the + will be passed to the method. + + + + + + Test if the logging event should we output by this appender + + the event to test + true if the event should be output, false if the event should be ignored + + + This method checks the logging event against the threshold level set + on this appender and also against the filters specified on this + appender. + + + The implementation of this method is as follows: + + + + + + Checks that the severity of the + is greater than or equal to the of this + appender. + + + + Checks that the chain accepts the + . + + + + + + + + + Adds a filter to the end of the filter chain. + + the filter to add to this appender + + + The Filters are organized in a linked list. + + + Setting this property causes the new filter to be pushed onto the + back of the filter chain. + + + + + + Clears the filter list for this appender. + + + + Clears the filter list for this appender. + + + + + + Checks if the message level is below this appender's threshold. + + to test against. + + + If there is no threshold set, then the return value is always true. + + + + true if the meets the + requirements of this appender. + + + + + Is called when the appender is closed. Derived classes should override + this method if resources need to be released. + + + + Releases any resources allocated within the appender such as file handles, + network connections, etc. + + + It is a programming error to append to a closed appender. + + + + + + Subclasses of should implement this method + to perform actual logging. + + The event to append. + + + A subclass must implement this method to perform + logging of the . + + This method will be called by + if all the conditions listed for that method are met. + + + To restrict the logging of events in the appender + override the method. + + + + + + Append a bulk array of logging events. + + the array of logging events + + + This base class implementation calls the + method for each element in the bulk array. + + + A sub class that can better process a bulk array of events should + override this method in addition to . + + + + + + Called before as a precondition. + + + + This method is called by + before the call to the abstract method. + + + This method can be overridden in a subclass to extend the checks + made before the event is passed to the method. + + + A subclass should ensure that they delegate this call to + this base class if it is overridden. + + + true if the call to should proceed. + + + + Renders the to a string. + + The event to render. + The event rendered as a string. + + + Helper method to render a to + a string. This appender must have a + set to render the to + a string. + + If there is exception data in the logging event and + the layout does not process the exception, this method + will append the exception text to the rendered string. + + + Where possible use the alternative version of this method + . + That method streams the rendering onto an existing Writer + which can give better performance if the caller already has + a open and ready for writing. + + + + + + Renders the to a string. + + The event to render. + The TextWriter to write the formatted event to + + + Helper method to render a to + a string. This appender must have a + set to render the to + a string. + + If there is exception data in the logging event and + the layout does not process the exception, this method + will append the exception text to the rendered string. + + + Use this method in preference to + where possible. If, however, the caller needs to render the event + to a string then does + provide an efficient mechanism for doing so. + + + + + + The layout of this appender. + + + See for more information. + + + + + The name of this appender. + + + See for more information. + + + + + The level threshold of this appender. + + + + There is no level threshold filtering by default. + + + See for more information. + + + + + + It is assumed and enforced that errorHandler is never null. + + + + It is assumed and enforced that errorHandler is never null. + + + See for more information. + + + + + + The first filter in the filter chain. + + + + Set to null initially. + + + See for more information. + + + + + + The last filter in the filter chain. + + + See for more information. + + + + + Flag indicating if this appender is closed. + + + See for more information. + + + + + The guard prevents an appender from repeatedly calling its own DoAppend method + + + + + StringWriter used to render events + + + + + Gets or sets the threshold of this appender. + + + The threshold of the appender. + + + + All log events with lower level than the threshold level are ignored + by the appender. + + + In configuration files this option is specified by setting the + value of the option to a level + string, such as "DEBUG", "INFO" and so on. + + + + + + Gets or sets the for this appender. + + The of the appender + + + The provides a default + implementation for the property. + + + + + + The filter chain. + + The head of the filter chain filter chain. + + + Returns the head Filter. The Filters are organized in a linked list + and so all Filters on this Appender are available through the result. + + + + + + Gets or sets the for this appender. + + The layout of the appender. + + + See for more information. + + + + + + + Gets or sets the name of this appender. + + The name of the appender. + + + The name uniquely identifies the appender. + + + + + + Tests if this appender requires a to be set. + + + + In the rather exceptional case, where the appender + implementation admits a layout but can also work without it, + then the appender should return true. + + + This default implementation always returns true. + + + + true if the appender requires a layout object, otherwise false. + + + + + The default buffer size. + + + The default size of the cyclic buffer used to store events. + This is set to 512 by default. + + + + + Initializes a new instance of the class. + + + + Protected default constructor to allow subclassing. + + + + + + Initializes a new instance of the class. + + the events passed through this appender must be + fixed by the time that they arrive in the derived class' SendBuffer method. + + + Protected constructor to allow subclassing. + + + The should be set if the subclass + expects the events delivered to be fixed even if the + is set to zero, i.e. when no buffering occurs. + + + + + + Flush the currently buffered events + + + + Flushes any events that have been buffered. + + + If the appender is buffering in mode then the contents + of the buffer will NOT be flushed to the appender. + + + + + + Flush the currently buffered events + + set to true to flush the buffer of lossy events + + + Flushes events that have been buffered. If is + false then events will only be flushed if this buffer is non-lossy mode. + + + If the appender is buffering in mode then the contents + of the buffer will only be flushed if is true. + In this case the contents of the buffer will be tested against the + and if triggering will be output. All other buffered + events will be discarded. + + + If is true then the buffer will always + be emptied by calling this method. + + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Close this appender instance. + + + + Close this appender instance. If this appender is marked + as not then the remaining events in + the buffer must be sent when the appender is closed. + + + + + + This method is called by the method. + + the event to log + + + Stores the in the cyclic buffer. + + + The buffer will be sent (i.e. passed to the + method) if one of the following conditions is met: + + + + The cyclic buffer is full and this appender is + marked as not lossy (see ) + + + An is set and + it is triggered for the + specified. + + + + Before the event is stored in the buffer it is fixed + (see ) to ensure that + any data referenced by the event will be valid when the buffer + is processed. + + + + + + Sends the contents of the buffer. + + The first logging event. + The buffer containing the events that need to be send. + + + The subclass must override . + + + + + + Sends the events. + + The events that need to be send. + + + The subclass must override this method to process the buffered events. + + + + + + The size of the cyclic buffer used to hold the logging events. + + + Set to by default. + + + + + The cyclic buffer used to store the logging events. + + + + + The triggering event evaluator that causes the buffer to be sent immediately. + + + The object that is used to determine if an event causes the entire + buffer to be sent immediately. This field can be null, which + indicates that event triggering is not to be done. The evaluator + can be set using the property. If this appender + has the ( property) set to + true then an must be set. + + + + + Indicates if the appender should overwrite events in the cyclic buffer + when it becomes full, or if the buffer should be flushed when the + buffer is full. + + + If this field is set to true then an must + be set. + + + + + The triggering event evaluator filters discarded events. + + + The object that is used to determine if an event that is discarded should + really be discarded or if it should be sent to the appenders. + This field can be null, which indicates that all discarded events will + be discarded. + + + + + Value indicating which fields in the event should be fixed + + + By default all fields are fixed + + + + + The events delivered to the subclass must be fixed. + + + + + Gets or sets a value that indicates whether the appender is lossy. + + + true if the appender is lossy, otherwise false. The default is false. + + + + This appender uses a buffer to store logging events before + delivering them. A triggering event causes the whole buffer + to be send to the remote sink. If the buffer overruns before + a triggering event then logging events could be lost. Set + to false to prevent logging events + from being lost. + + If is set to true then an + must be specified. + + + + + Gets or sets the size of the cyclic buffer used to hold the + logging events. + + + The size of the cyclic buffer used to hold the logging events. + + + + The option takes a positive integer + representing the maximum number of logging events to collect in + a cyclic buffer. When the is reached, + oldest events are deleted as new events are added to the + buffer. By default the size of the cyclic buffer is 512 events. + + + If the is set to a value less than + or equal to 1 then no buffering will occur. The logging event + will be delivered synchronously (depending on the + and properties). Otherwise the event will + be buffered. + + + + + + Gets or sets the that causes the + buffer to be sent immediately. + + + The that causes the buffer to be + sent immediately. + + + + The evaluator will be called for each event that is appended to this + appender. If the evaluator triggers then the current buffer will + immediately be sent (see ). + + If is set to true then an + must be specified. + + + + + Gets or sets the value of the to use. + + + The value of the to use. + + + + The evaluator will be called for each event that is discarded from this + appender. If the evaluator triggers then the current buffer will immediately + be sent (see ). + + + + + + Gets or sets a value indicating if only part of the logging event data + should be fixed. + + + true if the appender should only fix part of the logging event + data, otherwise false. The default is false. + + + + Setting this property to true will cause only part of the + event data to be fixed and serialized. This will improve performance. + + + See for more information. + + + + + + Gets or sets a the fields that will be fixed in the event + + + The event fields that will be fixed before the event is buffered + + + + The logging event needs to have certain thread specific values + captured before it can be buffered. See + for details. + + + + + + + Initializes a new instance of the class. + + + Public default constructor to initialize a new instance of this class. + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Override the parent method to close the database + + + + Closes the database command and database connection. + + + + + + Inserts the events into the database. + + The events to insert into the database. + + + Insert all the events specified in the + array into the database. + + + + + + Adds a parameter to the command. + + The parameter to add to the command. + + + Adds a parameter to the ordered list of command parameters. + + + + + + Writes the events to the database using the transaction specified. + + The transaction that the events will be executed under. + The array of events to insert into the database. + + + The transaction argument can be null if the appender has been + configured not to use transactions. See + property for more information. + + + + + + Formats the log message into database statement text. + + The event being logged. + + This method can be overridden by subclasses to provide + more control over the format of the database statement. + + + Text that can be passed to a . + + + + + Connects to the database. + + + + + Retrieves the class type of the ADO.NET provider. + + + + Gets the Type of the ADO.NET provider to use to connect to the + database. This method resolves the type specified in the + property. + + + Subclasses can override this method to return a different type + if necessary. + + + The of the ADO.NET provider + + + + Prepares the database command and initialize the parameters. + + + + + Flag to indicate if we are using a command object + + + + Set to true when the appender is to use a prepared + statement or stored procedure to insert into the database. + + + + + + The list of objects. + + + + The list of objects. + + + + + + The security context to use for privileged calls + + + + + The that will be used + to insert logging events into a database. + + + + + The database command. + + + + + Database connection string. + + + + + String type name of the type name. + + + + + The text of the command. + + + + + The command type. + + + + + Indicates whether to use transactions when writing to the database. + + + + + Indicates whether to use transactions when writing to the database. + + + + + Gets or sets the database connection string that is used to connect to + the database. + + + The database connection string used to connect to the database. + + + + The connections string is specific to the connection type. + See for more information. + + + Connection string for MS Access via ODBC: + "DSN=MS Access Database;UID=admin;PWD=;SystemDB=C:\data\System.mdw;SafeTransactions = 0;FIL=MS Access;DriverID = 25;DBQ=C:\data\train33.mdb" + + Another connection string for MS Access via ODBC: + "Driver={Microsoft Access Driver (*.mdb)};DBQ=C:\Work\cvs_root\log4net-1.2\access.mdb;UID=;PWD=;" + + Connection string for MS Access via OLE DB: + "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Work\cvs_root\log4net-1.2\access.mdb;User Id=;Password=;" + + + + + Gets or sets the type name of the connection + that should be created. + + + The type name of the connection. + + + + The type name of the ADO.NET provider to use. + + + The default is to use the OLE DB provider. + + + Use the OLE DB Provider. This is the default value. + System.Data.OleDb.OleDbConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Use the MS SQL Server Provider. + System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Use the ODBC Provider. + Microsoft.Data.Odbc.OdbcConnection,Microsoft.Data.Odbc,version=1.0.3300.0,publicKeyToken=b77a5c561934e089,culture=neutral + This is an optional package that you can download from + http://msdn.microsoft.com/downloads + search for ODBC .NET Data Provider. + + Use the Oracle Provider. + System.Data.OracleClient.OracleConnection, System.Data.OracleClient, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + This is an optional package that you can download from + http://msdn.microsoft.com/downloads + search for .NET Managed Provider for Oracle. + + + + + Gets or sets the command text that is used to insert logging events + into the database. + + + The command text used to insert logging events into the database. + + + + Either the text of the prepared statement or the + name of the stored procedure to execute to write into + the database. + + + The property determines if + this text is a prepared statement or a stored procedure. + + + + + + Gets or sets the command type to execute. + + + The command type to execute. + + + + This value may be either (System.Data.CommandType.Text) to specify + that the is a prepared statement to execute, + or (System.Data.CommandType.StoredProcedure) to specify that the + property is the name of a stored procedure + to execute. + + + The default value is (System.Data.CommandType.Text). + + + + + + Should transactions be used to insert logging events in the database. + + + true if transactions should be used to insert logging events in + the database, otherwise false. The default value is true. + + + + Gets or sets a value that indicates whether transactions should be used + to insert logging events in the database. + + + When set a single transaction will be used to insert the buffered events + into the database. Otherwise each event will be inserted without using + an explicit transaction. + + + + + + Gets or sets the used to call the NetSend method. + + + The used to call the NetSend method. + + + + Unless a specified here for this appender + the is queried for the + security context to use. The default behavior is to use the security context + of the current thread. + + + + + + Should this appender try to reconnect to the database on error. + + + true if the appender should try to reconnect to the database after an + error has occurred, otherwise false. The default value is false, + i.e. not to try to reconnect. + + + + The default behaviour is for the appender not to try to reconnect to the + database if an error occurs. Subsequent logging events are discarded. + + + To force the appender to attempt to reconnect to the database set this + property to true. + + + When the appender attempts to connect to the database there may be a + delay of up to the connection timeout specified in the connection string. + This delay will block the calling application's thread. + Until the connection can be reestablished this potential delay may occur multiple times. + + + + + + Gets or sets the underlying . + + + The underlying . + + + creates a to insert + logging events into a database. Classes deriving from + can use this property to get or set this . Use the + underlying returned from if + you require access beyond that which provides. + + + + + Parameter type used by the . + + + + This class provides the basic database parameter properties + as defined by the interface. + + This type can be subclassed to provide database specific + functionality. The two methods that are called externally are + and . + + + + + + Initializes a new instance of the class. + + + Default constructor for the AdoNetAppenderParameter class. + + + + + Prepare the specified database command object. + + The command to prepare. + + + Prepares the database command object by adding + this parameter to its collection of parameters. + + + + + + Renders the logging event and set the parameter value in the command. + + The command containing the parameter. + The event to be rendered. + + + Renders the logging event using this parameters layout + object. Sets the value of the parameter on the command object. + + + + + + The name of this parameter. + + + + + The database type for this parameter. + + + + + Flag to infer type rather than use the DbType + + + + + The precision for this parameter. + + + + + The scale for this parameter. + + + + + The size for this parameter. + + + + + The to use to render the + logging event into an object for this parameter. + + + + + Gets or sets the name of this parameter. + + + The name of this parameter. + + + + The name of this parameter. The parameter name + must match up to a named parameter to the SQL stored procedure + or prepared statement. + + + + + + Gets or sets the database type for this parameter. + + + The database type for this parameter. + + + + The database type for this parameter. This property should + be set to the database type from the + enumeration. See . + + + This property is optional. If not specified the ADO.NET provider + will attempt to infer the type from the value. + + + + + + + Gets or sets the precision for this parameter. + + + The precision for this parameter. + + + + The maximum number of digits used to represent the Value. + + + This property is optional. If not specified the ADO.NET provider + will attempt to infer the precision from the value. + + + + + + + Gets or sets the scale for this parameter. + + + The scale for this parameter. + + + + The number of decimal places to which Value is resolved. + + + This property is optional. If not specified the ADO.NET provider + will attempt to infer the scale from the value. + + + + + + + Gets or sets the size for this parameter. + + + The size for this parameter. + + + + The maximum size, in bytes, of the data within the column. + + + This property is optional. If not specified the ADO.NET provider + will attempt to infer the size from the value. + + + + + + + Gets or sets the to use to + render the logging event into an object for this + parameter. + + + The used to render the + logging event into an object for this parameter. + + + + The that renders the value for this + parameter. + + + The can be used to adapt + any into a + for use in the property. + + + + + + Appends logging events to the terminal using ANSI color escape sequences. + + + + AnsiColorTerminalAppender appends log events to the standard output stream + or the error output stream using a layout specified by the + user. It also allows the color of a specific level of message to be set. + + + This appender expects the terminal to understand the VT100 control set + in order to interpret the color codes. If the terminal or console does not + understand the control codes the behavior is not defined. + + + By default, all output is written to the console's standard output stream. + The property can be set to direct the output to the + error stream. + + + NOTE: This appender writes each message to the System.Console.Out or + System.Console.Error that is set at the time the event is appended. + Therefore it is possible to programmatically redirect the output of this appender + (for example NUnit does this to capture program output). While this is the desired + behavior of this appender it may have security implications in your application. + + + When configuring the ANSI colored terminal appender, a mapping should be + specified to map a logging level to a color. For example: + + + + + + + + + + + + + + + The Level is the standard log4net logging level and ForeColor and BackColor can be any + of the following values: + + Blue + Green + Red + White + Yellow + Purple + Cyan + + These color values cannot be combined together to make new colors. + + + The attributes can be any combination of the following: + + Brightforeground is brighter + Dimforeground is dimmer + Underscoremessage is underlined + Blinkforeground is blinking (does not work on all terminals) + Reverseforeground and background are reversed + Hiddenoutput is hidden + Strikethroughmessage has a line through it + + While any of these attributes may be combined together not all combinations + work well together, for example setting both Bright and Dim attributes makes + no sense. + + + Patrick Wagstrom + Nicko Cadell + + + + The to use when writing to the Console + standard output stream. + + + + The to use when writing to the Console + standard output stream. + + + + + + The to use when writing to the Console + standard error output stream. + + + + The to use when writing to the Console + standard error output stream. + + + + + + Ansi code to reset terminal + + + + + Initializes a new instance of the class. + + + The instance of the class is set up to write + to the standard output stream. + + + + + Add a mapping of level to color + + The mapping to add + + + Add a mapping to this appender. + Each mapping defines the foreground and background colours + for a level. + + + + + + This method is called by the method. + + The event to log. + + + Writes the event to the console. + + + The format of the output will depend on the appender's layout. + + + + + + Initialize the options for this appender + + + + Initialize the level to color mappings set on this appender. + + + + + + Flag to write output to the error stream rather than the standard output stream + + + + + Mapping from level object to color value + + + + + Target is the value of the console output stream. + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + The enum of possible display attributes + + + + The following flags can be combined together to + form the ANSI color attributes. + + + + + + + text is bright + + + + + text is dim + + + + + text is underlined + + + + + text is blinking + + + Not all terminals support this attribute + + + + + text and background colors are reversed + + + + + text is hidden + + + + + text is displayed with a strikethrough + + + + + The enum of possible foreground or background color values for + use with the color mapping method + + + + The output can be in one for the following ANSI colors. + + + + + + + color is black + + + + + color is red + + + + + color is green + + + + + color is yellow + + + + + color is blue + + + + + color is magenta + + + + + color is cyan + + + + + color is white + + + + + A class to act as a mapping between the level that a logging call is made at and + the color it should be displayed as. + + + + Defines the mapping between a level and the color it should be displayed in. + + + + + + An entry in the + + + + This is an abstract base class for types that are stored in the + object. + + + Nicko Cadell + + + + Default protected constructor + + + + Default protected constructor + + + + + + Initialize any options defined on this entry + + + + Should be overridden by any classes that need to initialise based on their options + + + + + + The level that is the key for this mapping + + + The that is the key for this mapping + + + + Get or set the that is the key for this + mapping subclass. + + + + + + Initialize the options for the object + + + + Combine the and together + and append the attributes. + + + + + + The mapped foreground color for the specified level + + + + Required property. + The mapped foreground color for the specified level + + + + + + The mapped background color for the specified level + + + + Required property. + The mapped background color for the specified level + + + + + + The color attributes for the specified level + + + + Required property. + The color attributes for the specified level + + + + + + The combined , and + suitable for setting the ansi terminal color. + + + + + A strongly-typed collection of objects. + + Nicko Cadell + + + + Creates a read-only wrapper for a AppenderCollection instance. + + list to create a readonly wrapper arround + + An AppenderCollection wrapper that is read-only. + + + + + An empty readonly static AppenderCollection + + + + + Initializes a new instance of the AppenderCollection class + that is empty and has the default initial capacity. + + + + + Initializes a new instance of the AppenderCollection class + that has the specified initial capacity. + + + The number of elements that the new AppenderCollection is initially capable of storing. + + + + + Initializes a new instance of the AppenderCollection class + that contains elements copied from the specified AppenderCollection. + + The AppenderCollection whose elements are copied to the new collection. + + + + Initializes a new instance of the AppenderCollection class + that contains elements copied from the specified array. + + The array whose elements are copied to the new list. + + + + Initializes a new instance of the AppenderCollection class + that contains elements copied from the specified collection. + + The collection whose elements are copied to the new list. + + + + Allow subclasses to avoid our default constructors + + + + + + + Copies the entire AppenderCollection to a one-dimensional + array. + + The one-dimensional array to copy to. + + + + Copies the entire AppenderCollection to a one-dimensional + array, starting at the specified index of the target array. + + The one-dimensional array to copy to. + The zero-based index in at which copying begins. + + + + Adds a to the end of the AppenderCollection. + + The to be added to the end of the AppenderCollection. + The index at which the value has been added. + + + + Removes all elements from the AppenderCollection. + + + + + Creates a shallow copy of the . + + A new with a shallow copy of the collection data. + + + + Determines whether a given is in the AppenderCollection. + + The to check for. + true if is found in the AppenderCollection; otherwise, false. + + + + Returns the zero-based index of the first occurrence of a + in the AppenderCollection. + + The to locate in the AppenderCollection. + + The zero-based index of the first occurrence of + in the entire AppenderCollection, if found; otherwise, -1. + + + + + Inserts an element into the AppenderCollection at the specified index. + + The zero-based index at which should be inserted. + The to insert. + + is less than zero + -or- + is equal to or greater than . + + + + + Removes the first occurrence of a specific from the AppenderCollection. + + The to remove from the AppenderCollection. + + The specified was not found in the AppenderCollection. + + + + + Removes the element at the specified index of the AppenderCollection. + + The zero-based index of the element to remove. + + is less than zero + -or- + is equal to or greater than . + + + + + Returns an enumerator that can iterate through the AppenderCollection. + + An for the entire AppenderCollection. + + + + Adds the elements of another AppenderCollection to the current AppenderCollection. + + The AppenderCollection whose elements should be added to the end of the current AppenderCollection. + The new of the AppenderCollection. + + + + Adds the elements of a array to the current AppenderCollection. + + The array whose elements should be added to the end of the AppenderCollection. + The new of the AppenderCollection. + + + + Adds the elements of a collection to the current AppenderCollection. + + The collection whose elements should be added to the end of the AppenderCollection. + The new of the AppenderCollection. + + + + Sets the capacity to the actual number of elements. + + + + + Return the collection elements as an array + + the array + + + + is less than zero + -or- + is equal to or greater than . + + + + + is less than zero + -or- + is equal to or greater than . + + + + + Gets the number of elements actually contained in the AppenderCollection. + + + + + Gets a value indicating whether access to the collection is synchronized (thread-safe). + + true if access to the ICollection is synchronized (thread-safe); otherwise, false. + + + + Gets an object that can be used to synchronize access to the collection. + + + + + Gets or sets the at the specified index. + + The zero-based index of the element to get or set. + + is less than zero + -or- + is equal to or greater than . + + + + + Gets a value indicating whether the collection has a fixed size. + + true if the collection has a fixed size; otherwise, false. The default is false + + + + Gets a value indicating whether the IList is read-only. + + true if the collection is read-only; otherwise, false. The default is false + + + + Gets or sets the number of elements the AppenderCollection can contain. + + + + + Supports type-safe iteration over a . + + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + + + Type visible only to our subclasses + Used to access protected constructor + + + + + + A value + + + + + Supports simple iteration over a . + + + + + + Initializes a new instance of the Enumerator class. + + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + + + + + + + Appends log events to the ASP.NET system. + + + + + Diagnostic information and tracing messages that you specify are appended to the output + of the page that is sent to the requesting browser. Optionally, you can view this information + from a separate trace viewer (Trace.axd) that displays trace information for every page in a + given application. + + + Trace statements are processed and displayed only when tracing is enabled. You can control + whether tracing is displayed to a page, to the trace viewer, or both. + + + The logging event is passed to the or + method depending on the level of the logging event. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Write the logging event to the ASP.NET trace + + the event to log + + + Write the logging event to the ASP.NET trace + HttpContext.Current.Trace + (). + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Buffers events and then forwards them to attached appenders. + + + + The events are buffered in this appender until conditions are + met to allow the appender to deliver the events to the attached + appenders. See for the + conditions that cause the buffer to be sent. + + The forwarding appender can be used to specify different + thresholds and filters for the same appender at different locations + within the hierarchy. + + + Nicko Cadell + Gert Driesen + + + + Interface for attaching appenders to objects. + + + + Interface for attaching, removing and retrieving appenders. + + + Nicko Cadell + Gert Driesen + + + + Attaches an appender. + + The appender to add. + + + Add the specified appender. The implementation may + choose to allow or deny duplicate appenders. + + + + + + Gets an attached appender with the specified name. + + The name of the appender to get. + + The appender with the name specified, or null if no appender with the + specified name is found. + + + + Returns an attached appender with the specified. + If no appender with the specified name is found null will be + returned. + + + + + + Removes all attached appenders. + + + + Removes and closes all attached appenders + + + + + + Removes the specified appender from the list of attached appenders. + + The appender to remove. + The appender removed from the list + + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + Removes the appender with the specified name from the list of appenders. + + The name of the appender to remove. + The appender removed from the list + + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + Gets all attached appenders. + + + A collection of attached appenders. + + + + Gets a collection of attached appenders. + If there are no attached appenders the + implementation should return an empty + collection rather than null. + + + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Closes the appender and releases resources. + + + + Releases any resources allocated within the appender such as file handles, + network connections, etc. + + + It is a programming error to append to a closed appender. + + + + + + Send the events. + + The events that need to be send. + + + Forwards the events to the attached appenders. + + + + + + Adds an to the list of appenders of this + instance. + + The to add to this appender. + + + If the specified is already in the list of + appenders, then it won't be added again. + + + + + + Looks for the appender with the specified name. + + The name of the appender to lookup. + + The appender with the specified name, or null. + + + + Get the named appender attached to this buffering appender. + + + + + + Removes all previously added appenders from this appender. + + + + This is useful when re-reading configuration information. + + + + + + Removes the specified appender from the list of appenders. + + The appender to remove. + The appender removed from the list + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + Removes the appender with the specified name from the list of appenders. + + The name of the appender to remove. + The appender removed from the list + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + Implementation of the interface + + + + + Gets the appenders contained in this appender as an + . + + + If no appenders can be found, then an + is returned. + + + A collection of the appenders in this appender. + + + + + Appends logging events to the console. + + + + ColoredConsoleAppender appends log events to the standard output stream + or the error output stream using a layout specified by the + user. It also allows the color of a specific type of message to be set. + + + By default, all output is written to the console's standard output stream. + The property can be set to direct the output to the + error stream. + + + NOTE: This appender writes directly to the application's attached console + not to the System.Console.Out or System.Console.Error TextWriter. + The System.Console.Out and System.Console.Error streams can be + programmatically redirected (for example NUnit does this to capture program output). + This appender will ignore these redirections because it needs to use Win32 + API calls to colorize the output. To respect these redirections the + must be used. + + + When configuring the colored console appender, mapping should be + specified to map a logging level to a color. For example: + + + + + + + + + + + + + + The Level is the standard log4net logging level and ForeColor and BackColor can be any + combination of the following values: + + Blue + Green + Red + White + Yellow + Purple + Cyan + HighIntensity + + + + Rick Hobbs + Nicko Cadell + + + + The to use when writing to the Console + standard output stream. + + + + The to use when writing to the Console + standard output stream. + + + + + + The to use when writing to the Console + standard error output stream. + + + + The to use when writing to the Console + standard error output stream. + + + + + + Initializes a new instance of the class. + + + The instance of the class is set up to write + to the standard output stream. + + + + + Initializes a new instance of the class + with the specified layout. + + the layout to use for this appender + + The instance of the class is set up to write + to the standard output stream. + + + + + Initializes a new instance of the class + with the specified layout. + + the layout to use for this appender + flag set to true to write to the console error stream + + When is set to true, output is written to + the standard error output stream. Otherwise, output is written to the standard + output stream. + + + + + Add a mapping of level to color - done by the config file + + The mapping to add + + + Add a mapping to this appender. + Each mapping defines the foreground and background colors + for a level. + + + + + + This method is called by the method. + + The event to log. + + + Writes the event to the console. + + + The format of the output will depend on the appender's layout. + + + + + + Initialize the options for this appender + + + + Initialize the level to color mappings set on this appender. + + + + + + Flag to write output to the error stream rather than the standard output stream + + + + + Mapping from level object to color value + + + + + The console output stream writer to write to + + + + This writer is not thread safe. + + + + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + The enum of possible color values for use with the color mapping method + + + + The following flags can be combined together to + form the colors. + + + + + + + color is blue + + + + + color is green + + + + + color is red + + + + + color is white + + + + + color is yellow + + + + + color is purple + + + + + color is cyan + + + + + color is intensified + + + + + A class to act as a mapping between the level that a logging call is made at and + the color it should be displayed as. + + + + Defines the mapping between a level and the color it should be displayed in. + + + + + + Initialize the options for the object + + + + Combine the and together. + + + + + + The mapped foreground color for the specified level + + + + Required property. + The mapped foreground color for the specified level. + + + + + + The mapped background color for the specified level + + + + Required property. + The mapped background color for the specified level. + + + + + + The combined and suitable for + setting the console color. + + + + + Appends logging events to the console. + + + + ConsoleAppender appends log events to the standard output stream + or the error output stream using a layout specified by the + user. + + + By default, all output is written to the console's standard output stream. + The property can be set to direct the output to the + error stream. + + + NOTE: This appender writes each message to the System.Console.Out or + System.Console.Error that is set at the time the event is appended. + Therefore it is possible to programmatically redirect the output of this appender + (for example NUnit does this to capture program output). While this is the desired + behavior of this appender it may have security implications in your application. + + + Nicko Cadell + Gert Driesen + + + + The to use when writing to the Console + standard output stream. + + + + The to use when writing to the Console + standard output stream. + + + + + + The to use when writing to the Console + standard error output stream. + + + + The to use when writing to the Console + standard error output stream. + + + + + + Initializes a new instance of the class. + + + The instance of the class is set up to write + to the standard output stream. + + + + + Initializes a new instance of the class + with the specified layout. + + the layout to use for this appender + + The instance of the class is set up to write + to the standard output stream. + + + + + Initializes a new instance of the class + with the specified layout. + + the layout to use for this appender + flag set to true to write to the console error stream + + When is set to true, output is written to + the standard error output stream. Otherwise, output is written to the standard + output stream. + + + + + This method is called by the method. + + The event to log. + + + Writes the event to the console. + + + The format of the output will depend on the appender's layout. + + + + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + Target is the value of the console output stream. + This is either "Console.Out" or "Console.Error". + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Appends log events to the system. + + + + The application configuration file can be used to control what listeners + are actually used. See the MSDN documentation for the + class for details on configuring the + debug system. + + + Events are written using the + method. The event's logger name is passed as the value for the category name to the Write method. + + + Nicko Cadell + + + + Initializes a new instance of the . + + + + Default constructor. + + + + + + Initializes a new instance of the + with a specified layout. + + The layout to use with this appender. + + + Obsolete constructor. + + + + + + Writes the logging event to the system. + + The event to log. + + + Writes the logging event to the system. + If is true then the + is called. + + + + + + Immediate flush means that the underlying writer or output stream + will be flushed at the end of each append operation. + + + + Immediate flush is slower but ensures that each append request is + actually written. If is set to + false, then there is a good chance that the last few + logs events are not actually written to persistent media if and + when the application crashes. + + + The default value is true. + + + + + Gets or sets a value that indicates whether the appender will + flush at the end of each write. + + + The default behavior is to flush at the end of each + write. If the option is set tofalse, then the underlying + stream can defer writing to physical medium to a later time. + + + Avoiding the flush operation at the end of each append results + in a performance gain of 10 to 20 percent. However, there is safety + trade-off involved in skipping flushing. Indeed, when flushing is + skipped, then it is likely that the last few log events will not + be recorded on disk when the application exits. This is a high + price to pay even for a 20% performance gain. + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Writes events to the system event log. + + + + The EventID of the event log entry can be + set using the EventLogEventID property () + on the . + + + There is a limit of 32K characters for an event log message + + + When configuring the EventLogAppender a mapping can be + specified to map a logging level to an event log entry type. For example: + + + <mapping> + <level value="ERROR" /> + <eventLogEntryType value="Error" /> + </mapping> + <mapping> + <level value="DEBUG" /> + <eventLogEntryType value="Information" /> + </mapping> + + + The Level is the standard log4net logging level and eventLogEntryType can be any value + from the enum, i.e.: + + Erroran error event + Warninga warning event + Informationan informational event + + + + Aspi Havewala + Douglas de la Torre + Nicko Cadell + Gert Driesen + Thomas Voss + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Initializes a new instance of the class + with the specified . + + The to use with this appender. + + + Obsolete constructor. + + + + + + Add a mapping of level to - done by the config file + + The mapping to add + + + Add a mapping to this appender. + Each mapping defines the event log entry type for a level. + + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Create an event log source + + + Uses different API calls under NET_2_0 + + + + + This method is called by the + method. + + the event to log + + Writes the event to the system event log using the + . + + If the event has an EventID property (see ) + set then this integer will be used as the event log event id. + + + There is a limit of 32K characters for an event log message + + + + + + Get the equivalent for a + + the Level to convert to an EventLogEntryType + The equivalent for a + + Because there are fewer applicable + values to use in logging levels than there are in the + this is a one way mapping. There is + a loss of information during the conversion. + + + + + The log name is the section in the event logs where the messages + are stored. + + + + + Name of the application to use when logging. This appears in the + application column of the event log named by . + + + + + The name of the machine which holds the event log. This is + currently only allowed to be '.' i.e. the current machine. + + + + + Mapping from level object to EventLogEntryType + + + + + The security context to use for privileged calls + + + + + The name of the log where messages will be stored. + + + The string name of the log where messages will be stored. + + + This is the name of the log as it appears in the Event Viewer + tree. The default value is to log into the Application + log, this is where most applications write their events. However + if you need a separate log for your application (or applications) + then you should set the appropriately. + This should not be used to distinguish your event log messages + from those of other applications, the + property should be used to distinguish events. This property should be + used to group together events into a single log. + + + + + + Property used to set the Application name. This appears in the + event logs when logging. + + + The string used to distinguish events from different sources. + + + Sets the event log source property. + + + + + This property is used to return the name of the computer to use + when accessing the event logs. Currently, this is the current + computer, denoted by a dot "." + + + The string name of the machine holding the event log that + will be logged into. + + + This property cannot be changed. It is currently set to '.' + i.e. the local machine. This may be changed in future. + + + + + Gets or sets the used to write to the EventLog. + + + The used to write to the EventLog. + + + + The system security context used to write to the EventLog. + + + Unless a specified here for this appender + the is queried for the + security context to use. The default behavior is to use the security context + of the current thread. + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + A class to act as a mapping between the level that a logging call is made at and + the color it should be displayed as. + + + + Defines the mapping between a level and its event log entry type. + + + + + + The for this entry + + + + Required property. + The for this entry + + + + + + Appends logging events to a file. + + + + Logging events are sent to the file specified by + the property. + + + The file can be opened in either append or overwrite mode + by specifying the property. + If the file path is relative it is taken as relative from + the application base directory. The file encoding can be + specified by setting the property. + + + The layout's and + values will be written each time the file is opened and closed + respectively. If the property is + then the file may contain multiple copies of the header and footer. + + + This appender will first try to open the file for writing when + is called. This will typically be during configuration. + If the file cannot be opened for writing the appender will attempt + to open the file again each time a message is logged to the appender. + If the file cannot be opened for writing when a message is logged then + the message will be discarded by this appender. + + + The supports pluggable file locking models via + the property. + The default behavior, implemented by + is to obtain an exclusive write lock on the file until this appender is closed. + The alternative model, , only holds a + write lock while the appender is writing a logging event. + + + Nicko Cadell + Gert Driesen + Rodrigo B. de Oliveira + Douglas de la Torre + Niall Daley + + + + Sends logging events to a . + + + + An Appender that writes to a . + + + This appender may be used stand alone if initialized with an appropriate + writer, however it is typically used as a base class for an appender that + can open a to write to. + + + Nicko Cadell + Gert Driesen + Douglas de la Torre + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Initializes a new instance of the class and + sets the output destination to a new initialized + with the specified . + + The layout to use with this appender. + The to output to. + + + Obsolete constructor. + + + + + + Initializes a new instance of the class and sets + the output destination to the specified . + + The layout to use with this appender + The to output to + + The must have been previously opened. + + + + Obsolete constructor. + + + + + + This method determines if there is a sense in attempting to append. + + + + This method checked if an output target has been set and if a + layout has been set. + + + false if any of the preconditions fail. + + + + This method is called by the + method. + + The event to log. + + + Writes a log statement to the output stream if the output stream exists + and is writable. + + + The format of the output will depend on the appender's layout. + + + + + + This method is called by the + method. + + The array of events to log. + + + This method writes all the bulk logged events to the output writer + before flushing the stream. + + + + + + Close this appender instance. The underlying stream or writer is also closed. + + + Closed appenders cannot be reused. + + + + + Writes the footer and closes the underlying . + + + + Writes the footer and closes the underlying . + + + + + + Closes the underlying . + + + + Closes the underlying . + + + + + + Clears internal references to the underlying + and other variables. + + + + Subclasses can override this method for an alternate closing behavior. + + + + + + Writes a footer as produced by the embedded layout's property. + + + + Writes a footer as produced by the embedded layout's property. + + + + + + Writes a header produced by the embedded layout's property. + + + + Writes a header produced by the embedded layout's property. + + + + + + Called to allow a subclass to lazily initialize the writer + + + + This method is called when an event is logged and the or + have not been set. This allows a subclass to + attempt to initialize the writer multiple times. + + + + + + This is the where logging events + will be written to. + + + + + Immediate flush means that the underlying + or output stream will be flushed at the end of each append operation. + + + + Immediate flush is slower but ensures that each append request is + actually written. If is set to + false, then there is a good chance that the last few + logging events are not actually persisted if and when the application + crashes. + + + The default value is true. + + + + + + Gets or set whether the appender will flush at the end + of each append operation. + + + + The default behavior is to flush at the end of each + append operation. + + + If this option is set to false, then the underlying + stream can defer persisting the logging event to a later + time. + + + + Avoiding the flush operation at the end of each append results in + a performance gain of 10 to 20 percent. However, there is safety + trade-off involved in skipping flushing. Indeed, when flushing is + skipped, then it is likely that the last few log events will not + be recorded on disk when the application exits. This is a high + price to pay even for a 20% performance gain. + + + + + Sets the where the log output will go. + + + + The specified must be open and writable. + + + The will be closed when the appender + instance is closed. + + + Note: Logging to an unopened will fail. + + + + + + Gets or set the and the underlying + , if any, for this appender. + + + The for this appender. + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Gets or sets the where logging events + will be written to. + + + The where logging events are written. + + + + This is the where logging events + will be written to. + + + + + + Default constructor + + + + Default constructor + + + + + + Construct a new appender using the layout, file and append mode. + + the layout to use with this appender + the full path to the file to write to + flag to indicate if the file should be appended to + + + Obsolete constructor. + + + + + + Construct a new appender using the layout and file specified. + The file will be appended to. + + the layout to use with this appender + the full path to the file to write to + + + Obsolete constructor. + + + + + + Activate the options on the file appender. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + This will cause the file to be opened. + + + + + + Closes any previously opened file and calls the parent's . + + + + Resets the filename and the file stream. + + + + + + Called to initialize the file writer + + + + Will be called for each logged message until the file is + successfully opened. + + + + + + This method is called by the + method. + + The event to log. + + + Writes a log statement to the output stream if the output stream exists + and is writable. + + + The format of the output will depend on the appender's layout. + + + + + + This method is called by the + method. + + The array of events to log. + + + Acquires the output file locks once before writing all the events to + the stream. + + + + + + Writes a footer as produced by the embedded layout's property. + + + + Writes a footer as produced by the embedded layout's property. + + + + + + Writes a header produced by the embedded layout's property. + + + + Writes a header produced by the embedded layout's property. + + + + + + Closes the underlying . + + + + Closes the underlying . + + + + + + Closes the previously opened file. + + + + Writes the to the file and then + closes the file. + + + + + + Sets and opens the file where the log output will go. The specified file must be writable. + + The path to the log file. Must be a fully qualified path. + If true will append to fileName. Otherwise will truncate fileName + + + Calls but guarantees not to throw an exception. + Errors are passed to the . + + + + + + Sets and opens the file where the log output will go. The specified file must be writable. + + The path to the log file. Must be a fully qualified path. + If true will append to fileName. Otherwise will truncate fileName + + + If there was already an opened file, then the previous file + is closed first. + + + This method will ensure that the directory structure + for the specified exists. + + + + + + Sets the quiet writer used for file output + + the file stream that has been opened for writing + + + This implementation of creates a + over the and passes it to the + method. + + + This method can be overridden by sub classes that want to wrap the + in some way, for example to encrypt the output + data using a System.Security.Cryptography.CryptoStream. + + + + + + Sets the quiet writer being used. + + the writer over the file stream that has been opened for writing + + + This method can be overridden by sub classes that want to + wrap the in some way. + + + + + + Convert a path into a fully qualified path. + + The path to convert. + The fully qualified path. + + + Converts the path specified to a fully + qualified path. If the path is relative it is + taken as relative from the application base + directory. + + + + + + Flag to indicate if we should append to the file + or overwrite the file. The default is to append. + + + + + The name of the log file. + + + + + The encoding to use for the file stream. + + + + + The security context to use for privileged calls + + + + + The stream to log to. Has added locking semantics + + + + + The locking model to use + + + + + Gets or sets the path to the file that logging will be written to. + + + The path to the file that logging will be written to. + + + + If the path is relative it is taken as relative from + the application base directory. + + + + + + Gets or sets a flag that indicates whether the file should be + appended to or overwritten. + + + Indicates whether the file should be appended to or overwritten. + + + + If the value is set to false then the file will be overwritten, if + it is set to true then the file will be appended to. + + The default value is true. + + + + + Gets or sets used to write to the file. + + + The used to write to the file. + + + + The default encoding set is + which is the encoding for the system's current ANSI code page. + + + + + + Gets or sets the used to write to the file. + + + The used to write to the file. + + + + Unless a specified here for this appender + the is queried for the + security context to use. The default behavior is to use the security context + of the current thread. + + + + + + Gets or sets the used to handle locking of the file. + + + The used to lock the file. + + + + Gets or sets the used to handle locking of the file. + + + There are two built in locking models, and . + The former locks the file from the start of logging to the end and the + later lock only for the minimal amount of time when logging each message. + + + The default locking model is the . + + + + + + Write only that uses the + to manage access to an underlying resource. + + + + + True asynchronous writes are not supported, the implementation forces a synchronous write. + + + + + Exception base type for log4net. + + + + This type extends . It + does not add any new functionality but does differentiate the + type of exception being thrown. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Constructor + + A message to include with the exception. + + + Initializes a new instance of the class with + the specified message. + + + + + + Constructor + + A message to include with the exception. + A nested exception to include. + + + Initializes a new instance of the class + with the specified message and inner exception. + + + + + + Serialization constructor + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + + + Initializes a new instance of the class + with serialized data. + + + + + + Locking model base class + + + + Base class for the locking models available to the derived loggers. + + + + + + Open the output file + + The filename to use + Whether to append to the file, or overwrite + The encoding to use + + + Open the file specified and prepare for logging. + No writes will be made until is called. + Must be called before any calls to , + and . + + + + + + Close the file + + + + Close the file. No further writes will be made. + + + + + + Acquire the lock on the file + + A stream that is ready to be written to. + + + Acquire the lock on the file in preparation for writing to it. + Return a stream pointing to the file. + must be called to release the lock on the output file. + + + + + + Release the lock on the file + + + + Release the lock on the file. No further writes will be made to the + stream until is called again. + + + + + + Gets or sets the for this LockingModel + + + The for this LockingModel + + + + The file appender this locking model is attached to and working on + behalf of. + + + The file appender is used to locate the security context and the error handler to use. + + + The value of this property will be set before is + called. + + + + + + Hold an exclusive lock on the output file + + + + Open the file once for writing and hold it open until is called. + Maintains an exclusive lock on the file during this time. + + + + + + Open the file specified and prepare for logging. + + The filename to use + Whether to append to the file, or overwrite + The encoding to use + + + Open the file specified and prepare for logging. + No writes will be made until is called. + Must be called before any calls to , + and . + + + + + + Close the file + + + + Close the file. No further writes will be made. + + + + + + Acquire the lock on the file + + A stream that is ready to be written to. + + + Does nothing. The lock is already taken + + + + + + Release the lock on the file + + + + Does nothing. The lock will be released when the file is closed. + + + + + + Acquires the file lock for each write + + + + Opens the file once for each / cycle, + thus holding the lock for the minimal amount of time. This method of locking + is considerably slower than but allows + other processes to move/delete the log file whilst logging continues. + + + + + + Prepares to open the file when the first message is logged. + + The filename to use + Whether to append to the file, or overwrite + The encoding to use + + + Open the file specified and prepare for logging. + No writes will be made until is called. + Must be called before any calls to , + and . + + + + + + Close the file + + + + Close the file. No further writes will be made. + + + + + + Acquire the lock on the file + + A stream that is ready to be written to. + + + Acquire the lock on the file in preparation for writing to it. + Return a stream pointing to the file. + must be called to release the lock on the output file. + + + + + + Release the lock on the file + + + + Release the lock on the file. No further writes will be made to the + stream until is called again. + + + + + + This appender forwards logging events to attached appenders. + + + + The forwarding appender can be used to specify different thresholds + and filters for the same appender at different locations within the hierarchy. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Closes the appender and releases resources. + + + + Releases any resources allocated within the appender such as file handles, + network connections, etc. + + + It is a programming error to append to a closed appender. + + + + + + Forward the logging event to the attached appenders + + The event to log. + + + Delivers the logging event to all the attached appenders. + + + + + + Forward the logging events to the attached appenders + + The array of events to log. + + + Delivers the logging events to all the attached appenders. + + + + + + Adds an to the list of appenders of this + instance. + + The to add to this appender. + + + If the specified is already in the list of + appenders, then it won't be added again. + + + + + + Looks for the appender with the specified name. + + The name of the appender to lookup. + + The appender with the specified name, or null. + + + + Get the named appender attached to this appender. + + + + + + Removes all previously added appenders from this appender. + + + + This is useful when re-reading configuration information. + + + + + + Removes the specified appender from the list of appenders. + + The appender to remove. + The appender removed from the list + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + Removes the appender with the specified name from the list of appenders. + + The name of the appender to remove. + The appender removed from the list + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + Implementation of the interface + + + + + Gets the appenders contained in this appender as an + . + + + If no appenders can be found, then an + is returned. + + + A collection of the appenders in this appender. + + + + + Logs events to a local syslog service. + + + + This appender uses the POSIX libc library functions openlog, syslog, and closelog. + If these functions are not available on the local system then this appender will not work! + + + The functions openlog, syslog, and closelog are specified in SUSv2 and + POSIX 1003.1-2001 standards. These are used to log messages to the local syslog service. + + + This appender talks to a local syslog service. If you need to log to a remote syslog + daemon and you cannot configure your local syslog service to do this you may be + able to use the to log via UDP. + + + Syslog messages must have a facility and and a severity. The severity + is derived from the Level of the logging event. + The facility must be chosen from the set of defined syslog + values. The facilities list is predefined + and cannot be extended. + + + An identifier is specified with each log message. This can be specified + by setting the property. The identity (also know + as the tag) must not contain white space. The default value for the + identity is the application name (from ). + + + Rob Lyon + Nicko Cadell + + + + Initializes a new instance of the class. + + + This instance of the class is set up to write + to a local syslog service. + + + + + Add a mapping of level to severity + + The mapping to add + + + Adds a to this appender. + + + + + + Initialize the appender based on the options set. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + This method is called by the method. + + The event to log. + + + Writes the event to a remote syslog daemon. + + + The format of the output will depend on the appender's layout. + + + + + + Close the syslog when the appender is closed + + + + Close the syslog when the appender is closed + + + + + + Translates a log4net level to a syslog severity. + + A log4net level. + A syslog severity. + + + Translates a log4net level to a syslog severity. + + + + + + Generate a syslog priority. + + The syslog facility. + The syslog severity. + A syslog priority. + + + + The facility. The default facility is . + + + + + The message identity + + + + + Marshaled handle to the identity string. We have to hold on to the + string as the openlog and syslog APIs just hold the + pointer to the ident and dereference it for each log message. + + + + + Mapping from level object to syslog severity + + + + + Open connection to system logger. + + + + + Generate a log message. + + + + The libc syslog method takes a format string and a variable argument list similar + to the classic printf function. As this type of vararg list is not supported + by C# we need to specify the arguments explicitly. Here we have specified the + format string with a single message argument. The caller must set the format + string to "%s". + + + + + + Close descriptor used to write to system logger. + + + + + Message identity + + + + An identifier is specified with each log message. This can be specified + by setting the property. The identity (also know + as the tag) must not contain white space. The default value for the + identity is the application name (from ). + + + + + + Syslog facility + + + Set to one of the values. The list of + facilities is predefined and cannot be extended. The default value + is . + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + syslog severities + + + + The log4net Level maps to a syslog severity using the + method and the + class. The severity is set on . + + + + + + system is unusable + + + + + action must be taken immediately + + + + + critical conditions + + + + + error conditions + + + + + warning conditions + + + + + normal but significant condition + + + + + informational + + + + + debug-level messages + + + + + syslog facilities + + + + The syslog facility defines which subsystem the logging comes from. + This is set on the property. + + + + + + kernel messages + + + + + random user-level messages + + + + + mail system + + + + + system daemons + + + + + security/authorization messages + + + + + messages generated internally by syslogd + + + + + line printer subsystem + + + + + network news subsystem + + + + + UUCP subsystem + + + + + clock (cron/at) daemon + + + + + security/authorization messages (private) + + + + + ftp daemon + + + + + NTP subsystem + + + + + log audit + + + + + log alert + + + + + clock daemon + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + A class to act as a mapping between the level that a logging call is made at and + the syslog severity that is should be logged at. + + + + A class to act as a mapping between the level that a logging call is made at and + the syslog severity that is should be logged at. + + + + + + The mapped syslog severity for the specified level + + + + Required property. + The mapped syslog severity for the specified level + + + + + + Stores logging events in an array. + + + + The memory appender stores all the logging events + that are appended in an in-memory array. + + + Use the method to get + the current list of events that have been appended. + + + Use the method to clear the + current list of events. + + + Julian Biddle + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Gets the events that have been logged. + + The events that have been logged + + + Gets the events that have been logged. + + + + + + This method is called by the method. + + the event to log + + Stores the in the events list. + + + + + Clear the list of events + + + Clear the list of events + + + + + The list of events that have been appended. + + + + + Value indicating which fields in the event should be fixed + + + By default all fields are fixed + + + + + Gets or sets a value indicating whether only part of the logging event + data should be fixed. + + + true if the appender should only fix part of the logging event + data, otherwise false. The default is false. + + + + Setting this property to true will cause only part of the event + data to be fixed and stored in the appender, hereby improving performance. + + + See for more information. + + + + + + Gets or sets the fields that will be fixed in the event + + + + The logging event needs to have certain thread specific values + captured before it can be buffered. See + for details. + + + + + + Logs entries by sending network messages using the + native function. + + + + You can send messages only to names that are active + on the network. If you send the message to a user name, + that user must be logged on and running the Messenger + service to receive the message. + + + The receiver will get a top most window displaying the + messages one at a time, therefore this appender should + not be used to deliver a high volume of messages. + + + The following table lists some possible uses for this appender : + + + + + Action + Property Value(s) + + + Send a message to a user account on the local machine + + + = <name of the local machine> + + + = <user name> + + + + + Send a message to a user account on a remote machine + + + = <name of the remote machine> + + + = <user name> + + + + + Send a message to a domain user account + + + = <name of a domain controller | uninitialized> + + + = <user name> + + + + + Send a message to all the names in a workgroup or domain + + + = <workgroup name | domain name>* + + + + + Send a message from the local machine to a remote machine + + + = <name of the local machine | uninitialized> + + + = <name of the remote machine> + + + + + + + Note : security restrictions apply for sending + network messages, see + for more information. + + + + + An example configuration section to log information + using this appender from the local machine, named + LOCAL_PC, to machine OPERATOR_PC : + + + + + + + + + + Nicko Cadell + Gert Driesen + + + + The DNS or NetBIOS name of the server on which the function is to execute. + + + + + The sender of the network message. + + + + + The message alias to which the message should be sent. + + + + + The security context to use for privileged calls + + + + + Initializes the appender. + + + The default constructor initializes all fields to their default values. + + + + + Initialize the appender based on the options set. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + The appender will be ignored if no was specified. + + + The required property was not specified. + + + + This method is called by the method. + + The event to log. + + + Sends the event using a network message. + + + + + + Sends a buffer of information to a registered message alias. + + The DNS or NetBIOS name of the server on which the function is to execute. + The message alias to which the message buffer should be sent + The originator of the message. + The message text. + The length, in bytes, of the message text. + + + The following restrictions apply for sending network messages: + + + + + Platform + Requirements + + + Windows NT + + + No special group membership is required to send a network message. + + + Admin, Accounts, Print, or Server Operator group membership is required to + successfully send a network message on a remote server. + + + + + Windows 2000 or later + + + If you send a message on a domain controller that is running Active Directory, + access is allowed or denied based on the access control list (ACL) for the securable + object. The default ACL permits only Domain Admins and Account Operators to send a network message. + + + On a member server or workstation, only Administrators and Server Operators can send a network message. + + + + + + + For more information see Security Requirements for the Network Management Functions. + + + + + If the function succeeds, the return value is zero. + + + + + + Gets or sets the sender of the message. + + + The sender of the message. + + + If this property is not specified, the message is sent from the local computer. + + + + + Gets or sets the message alias to which the message should be sent. + + + The recipient of the message. + + + This property should always be specified in order to send a message. + + + + + Gets or sets the DNS or NetBIOS name of the remote server on which the function is to execute. + + + DNS or NetBIOS name of the remote server on which the function is to execute. + + + + For Windows NT 4.0 and earlier, the string should begin with \\. + + + If this property is not specified, the local computer is used. + + + + + + Gets or sets the used to call the NetSend method. + + + The used to call the NetSend method. + + + + Unless a specified here for this appender + the is queried for the + security context to use. The default behavior is to use the security context + of the current thread. + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Appends log events to the OutputDebugString system. + + + + OutputDebugStringAppender appends log events to the + OutputDebugString system. + + + The string is passed to the native OutputDebugString + function. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Write the logging event to the output debug string API + + the event to log + + + Write the logging event to the output debug string API + + + + + + Stub for OutputDebugString native method + + the string to output + + + Stub for OutputDebugString native method + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Logs events to a remote syslog daemon. + + + + The BSD syslog protocol is used to remotely log to + a syslog daemon. The syslogd listens for for messages + on UDP port 514. + + + The syslog UDP protocol is not authenticated. Most syslog daemons + do not accept remote log messages because of the security implications. + You may be able to use the LocalSyslogAppender to talk to a local + syslog service. + + + There is an RFC 3164 that claims to document the BSD Syslog Protocol. + This RFC can be seen here: http://www.faqs.org/rfcs/rfc3164.html. + This appender generates what the RFC calls an "Original Device Message", + i.e. does not include the TIMESTAMP or HOSTNAME fields. By observation + this format of message will be accepted by all current syslog daemon + implementations. The daemon will attach the current time and the source + hostname or IP address to any messages received. + + + Syslog messages must have a facility and and a severity. The severity + is derived from the Level of the logging event. + The facility must be chosen from the set of defined syslog + values. The facilities list is predefined + and cannot be extended. + + + An identifier is specified with each log message. This can be specified + by setting the property. The identity (also know + as the tag) must not contain white space. The default value for the + identity is the application name (from ). + + + Rob Lyon + Nicko Cadell + + + + Sends logging events as connectionless UDP datagrams to a remote host or a + multicast group using an . + + + + UDP guarantees neither that messages arrive, nor that they arrive in the correct order. + + + To view the logging results, a custom application can be developed that listens for logging + events. + + + When decoding events send via this appender remember to use the same encoding + to decode the events as was used to send the events. See the + property to specify the encoding to use. + + + + This example shows how to log receive logging events that are sent + on IP address 244.0.0.1 and port 8080 to the console. The event is + encoded in the packet as a unicode string and it is decoded as such. + + IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + UdpClient udpClient; + byte[] buffer; + string loggingEvent; + + try + { + udpClient = new UdpClient(8080); + + while(true) + { + buffer = udpClient.Receive(ref remoteEndPoint); + loggingEvent = System.Text.Encoding.Unicode.GetString(buffer); + Console.WriteLine(loggingEvent); + } + } + catch(Exception e) + { + Console.WriteLine(e.ToString()); + } + + + Dim remoteEndPoint as IPEndPoint + Dim udpClient as UdpClient + Dim buffer as Byte() + Dim loggingEvent as String + + Try + remoteEndPoint = new IPEndPoint(IPAddress.Any, 0) + udpClient = new UdpClient(8080) + + While True + buffer = udpClient.Receive(ByRef remoteEndPoint) + loggingEvent = System.Text.Encoding.Unicode.GetString(buffer) + Console.WriteLine(loggingEvent) + Wend + Catch e As Exception + Console.WriteLine(e.ToString()) + End Try + + + An example configuration section to log information using this appender to the + IP 224.0.0.1 on port 8080: + + + + + + + + + + Gert Driesen + Nicko Cadell + + + + Initializes a new instance of the class. + + + The default constructor initializes all fields to their default values. + + + + + Initialize the appender based on the options set. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + The appender will be ignored if no was specified or + an invalid remote or local TCP port number was specified. + + + The required property was not specified. + The TCP port number assigned to or is less than or greater than . + + + + This method is called by the method. + + The event to log. + + + Sends the event using an UDP datagram. + + + Exceptions are passed to the . + + + + + + Closes the UDP connection and releases all resources associated with + this instance. + + + + Disables the underlying and releases all managed + and unmanaged resources associated with the . + + + + + + Initializes the underlying connection. + + + + The underlying is initialized and binds to the + port number from which you intend to communicate. + + + Exceptions are passed to the . + + + + + + The IP address of the remote host or multicast group to which + the logging event will be sent. + + + + + The TCP port number of the remote host or multicast group to + which the logging event will be sent. + + + + + The cached remote endpoint to which the logging events will be sent. + + + + + The TCP port number from which the will communicate. + + + + + The instance that will be used for sending the + logging events. + + + + + The encoding to use for the packet. + + + + + Gets or sets the IP address of the remote host or multicast group to which + the underlying should sent the logging event. + + + The IP address of the remote host or multicast group to which the logging event + will be sent. + + + + Multicast addresses are identified by IP class D addresses (in the range 224.0.0.0 to + 239.255.255.255). Multicast packets can pass across different networks through routers, so + it is possible to use multicasts in an Internet scenario as long as your network provider + supports multicasting. + + + Hosts that want to receive particular multicast messages must register their interest by joining + the multicast group. Multicast messages are not sent to networks where no host has joined + the multicast group. Class D IP addresses are used for multicast groups, to differentiate + them from normal host addresses, allowing nodes to easily detect if a message is of interest. + + + Static multicast addresses that are needed globally are assigned by IANA. A few examples are listed in the table below: + + + + + IP Address + Description + + + 224.0.0.1 + + + Sends a message to all system on the subnet. + + + + + 224.0.0.2 + + + Sends a message to all routers on the subnet. + + + + + 224.0.0.12 + + + The DHCP server answers messages on the IP address 224.0.0.12, but only on a subnet. + + + + + + + A complete list of actually reserved multicast addresses and their owners in the ranges + defined by RFC 3171 can be found at the IANA web site. + + + The address range 239.0.0.0 to 239.255.255.255 is reserved for administrative scope-relative + addresses. These addresses can be reused with other local groups. Routers are typically + configured with filters to prevent multicast traffic in this range from flowing outside + of the local network. + + + + + + Gets or sets the TCP port number of the remote host or multicast group to which + the underlying should sent the logging event. + + + An integer value in the range to + indicating the TCP port number of the remote host or multicast group to which the logging event + will be sent. + + + The underlying will send messages to this TCP port number + on the remote host or multicast group. + + The value specified is less than or greater than . + + + + Gets or sets the TCP port number from which the underlying will communicate. + + + An integer value in the range to + indicating the TCP port number from which the underlying will communicate. + + + + The underlying will bind to this port for sending messages. + + + Setting the value to 0 (the default) will cause the udp client not to bind to + a local port. + + + The value specified is less than or greater than . + + + + Gets or sets used to write the packets. + + + The used to write the packets. + + + + The used to write the packets. + + + + + + Gets or sets the underlying . + + + The underlying . + + + creates a to send logging events + over a network. Classes deriving from can use this + property to get or set this . Use the underlying + returned from if you require access beyond that which + provides. + + + + + Gets or sets the cached remote endpoint to which the logging events should be sent. + + + The cached remote endpoint to which the logging events will be sent. + + + The method will initialize the remote endpoint + with the values of the and + properties. + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Syslog port 514 + + + + + Initializes a new instance of the class. + + + This instance of the class is set up to write + to a remote syslog daemon. + + + + + Add a mapping of level to severity + + The mapping to add + + + Add a mapping to this appender. + + + + + + This method is called by the method. + + The event to log. + + + Writes the event to a remote syslog daemon. + + + The format of the output will depend on the appender's layout. + + + + + + Initialize the options for this appender + + + + Initialize the level to syslog severity mappings set on this appender. + + + + + + Translates a log4net level to a syslog severity. + + A log4net level. + A syslog severity. + + + Translates a log4net level to a syslog severity. + + + + + + Generate a syslog priority. + + The syslog facility. + The syslog severity. + A syslog priority. + + + Generate a syslog priority. + + + + + + The facility. The default facility is . + + + + + The message identity + + + + + Mapping from level object to syslog severity + + + + + Message identity + + + + An identifier is specified with each log message. This can be specified + by setting the property. The identity (also know + as the tag) must not contain white space. The default value for the + identity is the application name (from ). + + + + + + Syslog facility + + + Set to one of the values. The list of + facilities is predefined and cannot be extended. The default value + is . + + + + + syslog severities + + + + The syslog severities. + + + + + + system is unusable + + + + + action must be taken immediately + + + + + critical conditions + + + + + error conditions + + + + + warning conditions + + + + + normal but significant condition + + + + + informational + + + + + debug-level messages + + + + + syslog facilities + + + + The syslog facilities + + + + + + kernel messages + + + + + random user-level messages + + + + + mail system + + + + + system daemons + + + + + security/authorization messages + + + + + messages generated internally by syslogd + + + + + line printer subsystem + + + + + network news subsystem + + + + + UUCP subsystem + + + + + clock (cron/at) daemon + + + + + security/authorization messages (private) + + + + + ftp daemon + + + + + NTP subsystem + + + + + log audit + + + + + log alert + + + + + clock daemon + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + reserved for local use + + + + + A class to act as a mapping between the level that a logging call is made at and + the syslog severity that is should be logged at. + + + + A class to act as a mapping between the level that a logging call is made at and + the syslog severity that is should be logged at. + + + + + + The mapped syslog severity for the specified level + + + + Required property. + The mapped syslog severity for the specified level + + + + + + Delivers logging events to a remote logging sink. + + + + This Appender is designed to deliver events to a remote sink. + That is any object that implements the + interface. It delivers the events using .NET remoting. The + object to deliver events to is specified by setting the + appenders property. + + The RemotingAppender buffers events before sending them. This allows it to + make more efficient use of the remoting infrastructure. + + Once the buffer is full the events are still not sent immediately. + They are scheduled to be sent using a pool thread. The effect is that + the send occurs asynchronously. This is very important for a + number of non obvious reasons. The remoting infrastructure will + flow thread local variables (stored in the ), + if they are marked as , across the + remoting boundary. If the server is not contactable then + the remoting infrastructure will clear the + objects from the . To prevent a logging failure from + having side effects on the calling application the remoting call must be made + from a separate thread to the one used by the application. A + thread is used for this. If no thread is available then + the events will block in the thread pool manager until a thread is available. + + Because the events are sent asynchronously using pool threads it is possible to close + this appender before all the queued events have been sent. + When closing the appender attempts to wait until all the queued events have been sent, but + this will timeout after 30 seconds regardless. + + If this appender is being closed because the + event has fired it may not be possible to send all the queued events. During process + exit the runtime limits the time that a + event handler is allowed to run for. If the runtime terminates the threads before + the queued events have been sent then they will be lost. To ensure that all events + are sent the appender must be closed before the application exits. See + for details on how to shutdown + log4net programmatically. + + + Nicko Cadell + Gert Driesen + Daniel Cazzulino + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Send the contents of the buffer to the remote sink. + + + The events are not sent immediately. They are scheduled to be sent + using a pool thread. The effect is that the send occurs asynchronously. + This is very important for a number of non obvious reasons. The remoting + infrastructure will flow thread local variables (stored in the ), + if they are marked as , across the + remoting boundary. If the server is not contactable then + the remoting infrastructure will clear the + objects from the . To prevent a logging failure from + having side effects on the calling application the remoting call must be made + from a separate thread to the one used by the application. A + thread is used for this. If no thread is available then + the events will block in the thread pool manager until a thread is available. + + The events to send. + + + + Override base class close. + + + + This method waits while there are queued work items. The events are + sent asynchronously using work items. These items + will be sent once a thread pool thread is available to send them, therefore + it is possible to close the appender before all the queued events have been + sent. + + This method attempts to wait until all the queued events have been sent, but this + method will timeout after 30 seconds regardless. + + If the appender is being closed because the + event has fired it may not be possible to send all the queued events. During process + exit the runtime limits the time that a + event handler is allowed to run for. + + + + + A work item is being queued into the thread pool + + + + + A work item from the thread pool has completed + + + + + Send the contents of the buffer to the remote sink. + + + This method is designed to be used with the . + This method expects to be passed an array of + objects in the state param. + + the logging events to send + + + + The URL of the remote sink. + + + + + The local proxy (.NET remoting) for the remote logging sink. + + + + + The number of queued callbacks currently waiting or executing + + + + + Event used to signal when there are no queued work items + + + This event is set when there are no queued work items. In this + state it is safe to close the appender. + + + + + Gets or sets the URL of the well-known object that will accept + the logging events. + + + The well-known URL of the remote sink. + + + + The URL of the remoting sink that will accept logging events. + The sink must implement the + interface. + + + + + + Interface used to deliver objects to a remote sink. + + + This interface must be implemented by a remoting sink + if the is to be used + to deliver logging events to the sink. + + + + + Delivers logging events to the remote sink + + Array of events to log. + + + Delivers logging events to the remote sink + + + + + + Appender that rolls log files based on size or date or both. + + + + RollingFileAppender can roll log files based on size or date or both + depending on the setting of the property. + When set to the log file will be rolled + once its size exceeds the . + When set to the log file will be rolled + once the date boundary specified in the property + is crossed. + When set to the log file will be + rolled once the date boundary specified in the property + is crossed, but within a date boundary the file will also be rolled + once its size exceeds the . + When set to the log file will be rolled when + the appender is configured. This effectively means that the log file can be + rolled once per program execution. + + + A of few additional optional features have been added: + + Attach date pattern for current log file + Backup number increments for newer files + Infinite number of backups by file size + + + + + + For large or infinite numbers of backup files a + greater than zero is highly recommended, otherwise all the backup files need + to be renamed each time a new backup is created. + + + When Date/Time based rolling is used setting + to will reduce the number of file renamings to few or none. + + + + + + Changing or without clearing + the log file directory of backup files will cause unexpected and unwanted side effects. + + + + + If Date/Time based rolling is enabled this appender will attempt to roll existing files + in the directory without a Date/Time tag based on the last write date of the base log file. + The appender only rolls the log file when a message is logged. If Date/Time based rolling + is enabled then the appender will not roll the log file at the Date/Time boundary but + at the point when the next message is logged after the boundary has been crossed. + + + + The extends the and + has the same behavior when opening the log file. + The appender will first try to open the file for writing when + is called. This will typically be during configuration. + If the file cannot be opened for writing the appender will attempt + to open the file again each time a message is logged to the appender. + If the file cannot be opened for writing when a message is logged then + the message will be discarded by this appender. + + + When rolling a backup file necessitates deleting an older backup file the + file to be deleted is moved to a temporary name before being deleted. + + + + + A maximum number of backup files when rolling on date/time boundaries is not supported. + + + + Nicko Cadell + Gert Driesen + Aspi Havewala + Douglas de la Torre + Edward Smit + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Sets the quiet writer being used. + + + This method can be overridden by sub classes. + + the writer to set + + + + Write out a logging event. + + the event to write to file. + + + Handles append time behavior for RollingFileAppender. This checks + if a roll over either by date (checked first) or time (checked second) + is need and then appends to the file last. + + + + + + Write out an array of logging events. + + the events to write to file. + + + Handles append time behavior for RollingFileAppender. This checks + if a roll over either by date (checked first) or time (checked second) + is need and then appends to the file last. + + + + + + Performs any required rolling before outputting the next event + + + + Handles append time behavior for RollingFileAppender. This checks + if a roll over either by date (checked first) or time (checked second) + is need and then appends to the file last. + + + + + + Creates and opens the file for logging. If + is false then the fully qualified name is determined and used. + + the name of the file to open + true to append to existing file + + This method will ensure that the directory structure + for the specified exists. + + + + + Get the current output file name + + the base file name + the output file name + + The output file name is based on the base fileName specified. + If is set then the output + file name is the same as the base file passed in. Otherwise + the output file depends on the date pattern, on the count + direction or both. + + + + + Determines curSizeRollBackups (only within the current roll point) + + + + + Generates a wildcard pattern that can be used to find all files + that are similar to the base file name. + + + + + + + Builds a list of filenames for all files matching the base filename plus a file + pattern. + + + + + + + Initiates a roll over if needed for crossing a date boundary since the last run. + + + + + Initializes based on existing conditions at time of . + + + + Initializes based on existing conditions at time of . + The following is done + + determine curSizeRollBackups (only within the current roll point) + initiates a roll over if needed for crossing a date boundary since the last run. + + + + + + + Does the work of bumping the 'current' file counter higher + to the highest count when an incremental file name is seen. + The highest count is either the first file (when count direction + is greater than 0) or the last file (when count direction less than 0). + In either case, we want to know the highest count that is present. + + + + + + + Takes a list of files and a base file name, and looks for + 'incremented' versions of the base file. Bumps the max + count up to the highest count seen. + + + + + + + Calculates the RollPoint for the datePattern supplied. + + the date pattern to calculate the check period for + The RollPoint that is most accurate for the date pattern supplied + + Essentially the date pattern is examined to determine what the + most suitable roll point is. The roll point chosen is the roll point + with the smallest period that can be detected using the date pattern + supplied. i.e. if the date pattern only outputs the year, month, day + and hour then the smallest roll point that can be detected would be + and hourly roll point as minutes could not be detected. + + + + + Initialize the appender based on the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + Sets initial conditions including date/time roll over information, first check, + scheduledFilename, and calls to initialize + the current number of backups. + + + + + + Rollover the file(s) to date/time tagged file(s). + + set to true if the file to be rolled is currently open + + + Rollover the file(s) to date/time tagged file(s). + Resets curSizeRollBackups. + If fileIsOpen is set then the new file is opened (through SafeOpenFile). + + + + + + Renames file to file . + + Name of existing file to roll. + New name for file. + + + Renames file to file . It + also checks for existence of target file and deletes if it does. + + + + + + Test if a file exists at a specified path + + the path to the file + true if the file exists + + + Test if a file exists at a specified path + + + + + + Deletes the specified file if it exists. + + The file to delete. + + + Delete a file if is exists. + The file is first moved to a new filename then deleted. + This allows the file to be removed even when it cannot + be deleted, but it still can be moved. + + + + + + Implements file roll base on file size. + + + + If the maximum number of size based backups is reached + (curSizeRollBackups == maxSizeRollBackups) then the oldest + file is deleted -- its index determined by the sign of countDirection. + If countDirection < 0, then files + {File.1, ..., File.curSizeRollBackups -1} + are renamed to {File.2, ..., + File.curSizeRollBackups}. Moreover, File is + renamed File.1 and closed. + + + A new file is created to receive further log output. + + + If maxSizeRollBackups is equal to zero, then the + File is truncated with no backup files created. + + + If maxSizeRollBackups < 0, then File is + renamed if needed and no files are deleted. + + + + + + Implements file roll. + + the base name to rename + + + If the maximum number of size based backups is reached + (curSizeRollBackups == maxSizeRollBackups) then the oldest + file is deleted -- its index determined by the sign of countDirection. + If countDirection < 0, then files + {File.1, ..., File.curSizeRollBackups -1} + are renamed to {File.2, ..., + File.curSizeRollBackups}. + + + If maxSizeRollBackups is equal to zero, then the + File is truncated with no backup files created. + + + If maxSizeRollBackups < 0, then File is + renamed if needed and no files are deleted. + + + This is called by to rename the files. + + + + + + Get the start time of the next window for the current rollpoint + + the current date + the type of roll point we are working with + the start time for the next roll point an interval after the currentDateTime date + + + Returns the date of the next roll point after the currentDateTime date passed to the method. + + + The basic strategy is to subtract the time parts that are less significant + than the rollpoint from the current time. This should roll the time back to + the start of the time window for the current rollpoint. Then we add 1 window + worth of time and get the start time of the next window for the rollpoint. + + + + + + This object supplies the current date/time. Allows test code to plug in + a method to control this class when testing date/time based rolling. + + + + + The date pattern. By default, the pattern is set to ".yyyy-MM-dd" + meaning daily rollover. + + + + + The actual formatted filename that is currently being written to + or will be the file transferred to on roll over + (based on staticLogFileName). + + + + + The timestamp when we shall next recompute the filename. + + + + + Holds date of last roll over + + + + + The type of rolling done + + + + + The default maximum file size is 10MB + + + + + There is zero backup files by default + + + + + How many sized based backups have been made so far + + + + + The rolling file count direction. + + + + + The rolling mode used in this appender. + + + + + Cache flag set if we are rolling by date. + + + + + Cache flag set if we are rolling by size. + + + + + Value indicating whether to always log to the same file. + + + + + FileName provided in configuration. Used for rolling properly + + + + + The 1st of January 1970 in UTC + + + + + Gets or sets the date pattern to be used for generating file names + when rolling over on date. + + + The date pattern to be used for generating file names when rolling + over on date. + + + + Takes a string in the same format as expected by + . + + + This property determines the rollover schedule when rolling over + on date. + + + + + + Gets or sets the maximum number of backup files that are kept before + the oldest is erased. + + + The maximum number of backup files that are kept before the oldest is + erased. + + + + If set to zero, then there will be no backup files and the log file + will be truncated when it reaches . + + + If a negative number is supplied then no deletions will be made. Note + that this could result in very slow performance as a large number of + files are rolled over unless is used. + + + The maximum applies to each time based group of files and + not the total. + + + + + + Gets or sets the maximum size that the output file is allowed to reach + before being rolled over to backup files. + + + The maximum size in bytes that the output file is allowed to reach before being + rolled over to backup files. + + + + This property is equivalent to except + that it is required for differentiating the setter taking a + argument from the setter taking a + argument. + + + The default maximum file size is 10MB (10*1024*1024). + + + + + + Gets or sets the maximum size that the output file is allowed to reach + before being rolled over to backup files. + + + The maximum size that the output file is allowed to reach before being + rolled over to backup files. + + + + This property allows you to specify the maximum size with the + suffixes "KB", "MB" or "GB" so that the size is interpreted being + expressed respectively in kilobytes, megabytes or gigabytes. + + + For example, the value "10KB" will be interpreted as 10240 bytes. + + + The default maximum file size is 10MB. + + + If you have the option to set the maximum file size programmatically + consider using the property instead as this + allows you to set the size in bytes as a . + + + + + + Gets or sets the rolling file count direction. + + + The rolling file count direction. + + + + Indicates if the current file is the lowest numbered file or the + highest numbered file. + + + By default newer files have lower numbers ( < 0), + i.e. log.1 is most recent, log.5 is the 5th backup, etc... + + + >= 0 does the opposite i.e. + log.1 is the first backup made, log.5 is the 5th backup made, etc. + For infinite backups use >= 0 to reduce + rollover costs. + + The default file count direction is -1. + + + + + Gets or sets the rolling style. + + The rolling style. + + + The default rolling style is . + + + When set to this appender's + property is set to false, otherwise + the appender would append to a single file rather than rolling + the file each time it is opened. + + + + + + Gets or sets a value indicating whether to always log to + the same file. + + + true if always should be logged to the same file, otherwise false. + + + + By default file.log is always the current file. Optionally + file.log.yyyy-mm-dd for current formatted datePattern can by the currently + logging file (or file.log.curSizeRollBackup or even + file.log.yyyy-mm-dd.curSizeRollBackup). + + + This will make time based rollovers with a large number of backups + much faster as the appender it won't have to rename all the backups! + + + + + + Style of rolling to use + + + + Style of rolling to use + + + + + + Roll files once per program execution + + + + Roll files once per program execution. + Well really once each time this appender is + configured. + + + Setting this option also sets AppendToFile to + false on the RollingFileAppender, otherwise + this appender would just be a normal file appender. + + + + + + Roll files based only on the size of the file + + + + + Roll files based only on the date + + + + + Roll files based on both the size and date of the file + + + + + The code assumes that the following 'time' constants are in a increasing sequence. + + + + The code assumes that the following 'time' constants are in a increasing sequence. + + + + + + Roll the log not based on the date + + + + + Roll the log for each minute + + + + + Roll the log for each hour + + + + + Roll the log twice a day (midday and midnight) + + + + + Roll the log each day (midnight) + + + + + Roll the log each week + + + + + Roll the log each month + + + + + This interface is used to supply Date/Time information to the . + + + This interface is used to supply Date/Time information to the . + Used primarily to allow test classes to plug themselves in so they can + supply test date/times. + + + + + Gets the current time. + + The current time. + + + Gets the current time. + + + + + + Default implementation of that returns the current time. + + + + + Gets the current time. + + The current time. + + + Gets the current time. + + + + + + Send an e-mail when a specific logging event occurs, typically on errors + or fatal errors. + + + + The number of logging events delivered in this e-mail depend on + the value of option. The + keeps only the last + logging events in its + cyclic buffer. This keeps memory requirements at a reasonable level while + still delivering useful application context. + + + Authentication and setting the server Port are only available on the MS .NET 1.1 runtime. + For these features to be enabled you need to ensure that you are using a version of + the log4net assembly that is built against the MS .NET 1.1 framework and that you are + running the your application on the MS .NET 1.1 runtime. On all other platforms only sending + unauthenticated messages to a server listening on port 25 (the default) is supported. + + + Authentication is supported by setting the property to + either or . + If using authentication then the + and properties must also be set. + + + To set the SMTP server port use the property. The default port is 25. + + + Nicko Cadell + Gert Driesen + + + + Default constructor + + + + Default constructor + + + + + + Sends the contents of the cyclic buffer as an e-mail message. + + The logging events to send. + + + + Send the email message + + the body text to include in the mail + + + + Gets or sets a semicolon-delimited list of recipient e-mail addresses. + + + A semicolon-delimited list of e-mail addresses. + + + + A semicolon-delimited list of recipient e-mail addresses. + + + + + + Gets or sets the e-mail address of the sender. + + + The e-mail address of the sender. + + + + The e-mail address of the sender. + + + + + + Gets or sets the subject line of the e-mail message. + + + The subject line of the e-mail message. + + + + The subject line of the e-mail message. + + + + + + Gets or sets the name of the SMTP relay mail server to use to send + the e-mail messages. + + + The name of the e-mail relay server. If SmtpServer is not set, the + name of the local SMTP server is used. + + + + The name of the e-mail relay server. If SmtpServer is not set, the + name of the local SMTP server is used. + + + + + + Obsolete + + + Use the BufferingAppenderSkeleton Fix methods instead + + + + Obsolete property. + + + + + + The mode to use to authentication with the SMTP server + + + Authentication is only available on the MS .NET 1.1 runtime. + + Valid Authentication mode values are: , + , and . + The default value is . When using + you must specify the + and to use to authenticate. + When using the Windows credentials for the current + thread, if impersonating, or the process will be used to authenticate. + + + + + + The username to use to authenticate with the SMTP server + + + Authentication is only available on the MS .NET 1.1 runtime. + + A and must be specified when + is set to , + otherwise the username will be ignored. + + + + + + The password to use to authenticate with the SMTP server + + + Authentication is only available on the MS .NET 1.1 runtime. + + A and must be specified when + is set to , + otherwise the password will be ignored. + + + + + + The port on which the SMTP server is listening + + + Server Port is only available on the MS .NET 1.1 runtime. + + The port on which the SMTP server is listening. The default + port is 25. The Port can only be changed when running on + the MS .NET 1.1 runtime. + + + + + + Gets or sets the priority of the e-mail message + + + One of the values. + + + + Sets the priority of the e-mails generated by this + appender. The default priority is . + + + If you are using this appender to report errors then + you may want to set the priority to . + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Values for the property. + + + + SMTP authentication modes. + + + + + + No authentication + + + + + Basic authentication. + + + Requires a username and password to be supplied + + + + + Integrated authentication + + + Uses the Windows credentials from the current thread or process to authenticate. + + + + + Send an email when a specific logging event occurs, typically on errors + or fatal errors. Rather than sending via smtp it writes a file into the + directory specified by . This allows services such + as the IIS SMTP agent to manage sending the messages. + + + + The configuration for this appender is identical to that of the SMTPAppender, + except that instead of specifying the SMTPAppender.SMTPHost you specify + . + + + The number of logging events delivered in this e-mail depend on + the value of option. The + keeps only the last + logging events in its + cyclic buffer. This keeps memory requirements at a reasonable level while + still delivering useful application context. + + + Niall Daley + Nicko Cadell + + + + Default constructor + + + + Default constructor + + + + + + Sends the contents of the cyclic buffer as an e-mail message. + + The logging events to send. + + + Sends the contents of the cyclic buffer as an e-mail message. + + + + + + Activate the options on this appender. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Convert a path into a fully qualified path. + + The path to convert. + The fully qualified path. + + + Converts the path specified to a fully + qualified path. If the path is relative it is + taken as relative from the application base + directory. + + + + + + The security context to use for privileged calls + + + + + Gets or sets a semicolon-delimited list of recipient e-mail addresses. + + + A semicolon-delimited list of e-mail addresses. + + + + A semicolon-delimited list of e-mail addresses. + + + + + + Gets or sets the e-mail address of the sender. + + + The e-mail address of the sender. + + + + The e-mail address of the sender. + + + + + + Gets or sets the subject line of the e-mail message. + + + The subject line of the e-mail message. + + + + The subject line of the e-mail message. + + + + + + Gets or sets the path to write the messages to. + + + + Gets or sets the path to write the messages to. This should be the same + as that used by the agent sending the messages. + + + + + + Gets or sets the used to write to the pickup directory. + + + The used to write to the pickup directory. + + + + Unless a specified here for this appender + the is queried for the + security context to use. The default behavior is to use the security context + of the current thread. + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Appender that allows clients to connect via Telnet to receive log messages + + + + The TelnetAppender accepts socket connections and streams logging messages + back to the client. + The output is provided in a telnet-friendly way so that a log can be monitored + over a TCP/IP socket. + This allows simple remote monitoring of application logging. + + + The default is 23 (the telnet port). + + + Keith Long + Nicko Cadell + + + + Default constructor + + + + Default constructor + + + + + + Overrides the parent method to close the socket handler + + + + Closes all the outstanding connections. + + + + + + Initialize the appender based on the options set. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + Create the socket handler and wait for connections + + + + + + Writes the logging event to each connected client. + + The event to log. + + + Writes the logging event to each connected client. + + + + + + Gets or sets the TCP port number on which this will listen for connections. + + + An integer value in the range to + indicating the TCP port number on which this will listen for connections. + + + + The default value is 23 (the telnet port). + + + The value specified is less than + or greater than . + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Helper class to manage connected clients + + + + The SocketHandler class is used to accept connections from + clients. It is threaded so that clients can connect/disconnect + asynchronously. + + + + + + Opens a new server port on + + the local port to listen on for connections + + + Creates a socket handler on the specified local server port. + + + + + + Sends a string message to each of the connected clients + + the text to send + + + Sends a string message to each of the connected clients + + + + + + Add a client to the internal clients list + + client to add + + + + Remove a client from the internal clients list + + client to remove + + + + Callback used to accept a connection on the server socket + + The result of the asynchronous operation + + + On connection adds to the list of connections + if there are two many open connections you will be disconnected + + + + + + Close all network connections + + + + Make sure we close all network connections + + + + + + Test if this handler has active connections + + + true if this handler has active connections + + + + This property will be true while this handler has + active connections, that is at least one connection that + the handler will attempt to send a message to. + + + + + + Class that represents a client connected to this handler + + + + Class that represents a client connected to this handler + + + + + + Create this for the specified + + the client's socket + + + Opens a stream writer on the socket. + + + + + + Write a string to the client + + string to send + + + Write a string to the client + + + + + + Cleanup the clients connection + + + + Close the socket connection. + + + + + + Appends log events to the system. + + + + The application configuration file can be used to control what listeners + are actually used. See the MSDN documentation for the + class for details on configuring the + trace system. + + + Events are written using the System.Diagnostics.Trace.Write(string,string) + method. The event's logger name is passed as the value for the category name to the Write method. + + + Compact Framework
+ The Compact Framework does not support the + class for any operation except Assert. When using the Compact Framework this + appender will write to the system rather than + the Trace system. This appender will therefore behave like the . +
+
+ Douglas de la Torre + Nicko Cadell + Gert Driesen +
+ + + Initializes a new instance of the . + + + + Default constructor. + + + + + + Initializes a new instance of the + with a specified layout. + + The layout to use with this appender. + + + Obsolete constructor. + + + + + + Writes the logging event to the system. + + The event to log. + + + Writes the logging event to the system. + + + + + + Immediate flush means that the underlying writer or output stream + will be flushed at the end of each append operation. + + + + Immediate flush is slower but ensures that each append request is + actually written. If is set to + false, then there is a good chance that the last few + logs events are not actually written to persistent media if and + when the application crashes. + + + The default value is true. + + + + + Gets or sets a value that indicates whether the appender will + flush at the end of each write. + + + The default behavior is to flush at the end of each + write. If the option is set tofalse, then the underlying + stream can defer writing to physical medium to a later time. + + + Avoiding the flush operation at the end of each append results + in a performance gain of 10 to 20 percent. However, there is safety + trade-off involved in skipping flushing. Indeed, when flushing is + skipped, then it is likely that the last few log events will not + be recorded on disk when the application exits. This is a high + price to pay even for a 20% performance gain. + + + + + + This appender requires a to be set. + + true + + + This appender requires a to be set. + + + + + + Assembly level attribute that specifies a domain to alias to this assembly's repository. + + + + AliasDomainAttribute is obsolete. Use AliasRepositoryAttribute instead of AliasDomainAttribute. + + + An assembly's logger repository is defined by its , + however this can be overridden by an assembly loaded before the target assembly. + + + An assembly can alias another assembly's domain to its repository by + specifying this attribute with the name of the target domain. + + + This attribute can only be specified on the assembly and may be used + as many times as necessary to alias all the required domains. + + + Nicko Cadell + Gert Driesen + + + + Assembly level attribute that specifies a repository to alias to this assembly's repository. + + + + An assembly's logger repository is defined by its , + however this can be overridden by an assembly loaded before the target assembly. + + + An assembly can alias another assembly's repository to its repository by + specifying this attribute with the name of the target repository. + + + This attribute can only be specified on the assembly and may be used + as many times as necessary to alias all the required repositories. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class with + the specified repository to alias to this assembly's repository. + + The repository to alias to this assemby's repository. + + + Initializes a new instance of the class with + the specified repository to alias to this assembly's repository. + + + + + + Gets or sets the repository to alias to this assemby's repository. + + + The repository to alias to this assemby's repository. + + + + The name of the repository to alias to this assemby's repository. + + + + + + Initializes a new instance of the class with + the specified domain to alias to this assembly's repository. + + The domain to alias to this assemby's repository. + + + Obsolete. Use instead of . + + + + + + Use this class to quickly configure a . + + + + Allows very simple programmatic configuration of log4net. + + + Only one appender can be configured using this configurator. + The appender is set at the root of the hierarchy and all logging + events will be delivered to that appender. + + + Appenders can also implement the interface. Therefore + they would require that the method + be called after the appenders properties have been configured. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + Initializes the log4net system with a default configuration. + + + + Initializes the log4net logging system using a + that will write to Console.Out. The log messages are + formatted using the layout object + with the + layout style. + + + + + + Initializes the log4net system using the specified appender. + + The appender to use to log all logging events. + + + Initializes the log4net system using the specified appender. + + + + + + Initializes the with a default configuration. + + The repository to configure. + + + Initializes the specified repository using a + that will write to Console.Out. The log messages are + formatted using the layout object + with the + layout style. + + + + + + Initializes the using the specified appender. + + The repository to configure. + The appender to use to log all logging events. + + + Initializes the using the specified appender. + + + + + + Base class for all log4net configuration attributes. + + + This is an abstract class that must be extended by + specific configurators. This attribute allows the + configurator to be parameterized by an assembly level + attribute. + + Nicko Cadell + Gert Driesen + + + + Constructor used by subclasses. + + the ordering priority for this configurator + + + The is used to order the configurator + attributes before they are invoked. Higher priority configurators are executed + before lower priority ones. + + + + + + Configures the for the specified assembly. + + The assembly that this attribute was defined on. + The repository to configure. + + + Abstract method implemented by a subclass. When this method is called + the subclass should configure the . + + + + + + Compare this instance to another ConfiguratorAttribute + + the object to compare to + see + + + Compares the priorities of the two instances. + Sorts by priority in descending order. Objects with the same priority are + randomly ordered. + + + + + + Assembly level attribute that specifies the logging domain for the assembly. + + + + DomainAttribute is obsolete. Use RepositoryAttribute instead of DomainAttribute. + + + Assemblies are mapped to logging domains. Each domain has its own + logging repository. This attribute specified on the assembly controls + the configuration of the domain. The property specifies the name + of the domain that this assembly is a part of. The + specifies the type of the repository objects to create for the domain. If + this attribute is not specified and a is not specified + then the assembly will be part of the default shared logging domain. + + + This attribute can only be specified on the assembly and may only be used + once per assembly. + + + Nicko Cadell + Gert Driesen + + + + Assembly level attribute that specifies the logging repository for the assembly. + + + + Assemblies are mapped to logging repository. This attribute specified + on the assembly controls + the configuration of the repository. The property specifies the name + of the repository that this assembly is a part of. The + specifies the type of the object + to create for the assembly. If this attribute is not specified or a + is not specified then the assembly will be part of the default shared logging repository. + + + This attribute can only be specified on the assembly and may only be used + once per assembly. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Initialize a new instance of the class + with the name of the repository. + + The name of the repository. + + + Initialize the attribute with the name for the assembly's repository. + + + + + + Gets or sets the name of the logging repository. + + + The string name to use as the name of the repository associated with this + assembly. + + + + This value does not have to be unique. Several assemblies can share the + same repository. They will share the logging configuration of the repository. + + + + + + Gets or sets the type of repository to create for this assembly. + + + The type of repository to create for this assembly. + + + + The type of the repository to create for the assembly. + The type must implement the + interface. + + + This will be the type of repository created when + the repository is created. If multiple assemblies reference the + same repository then the repository is only created once using the + of the first assembly to call into the + repository. + + + + + + Initializes a new instance of the class. + + + + Obsolete. Use RepositoryAttribute instead of DomainAttribute. + + + + + + Initialize a new instance of the class + with the name of the domain. + + The name of the domain. + + + Obsolete. Use RepositoryAttribute instead of DomainAttribute. + + + + + + Use this class to initialize the log4net environment using an Xml tree. + + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + Configures a using an Xml tree. + + + Nicko Cadell + Gert Driesen + + + + Private constructor + + + + + Automatically configures the log4net system based on the + application's configuration settings. + + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + Each application has a configuration file. This has the + same name as the application with '.config' appended. + This file is XML and calling this function prompts the + configurator to look in that file for a section called + log4net that contains the configuration data. + + + + + Automatically configures the using settings + stored in the application's configuration file. + + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + Each application has a configuration file. This has the + same name as the application with '.config' appended. + This file is XML and calling this function prompts the + configurator to look in that file for a section called + log4net that contains the configuration data. + + The repository to configure. + + + + Configures log4net using a log4net element + + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + Loads the log4net configuration from the XML element + supplied as . + + The element to parse. + + + + Configures the using the specified XML + element. + + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + Loads the log4net configuration from the XML element + supplied as . + + The repository to configure. + The element to parse. + + + + Configures log4net using the specified configuration file. + + The XML file to load the configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the log4net configuration data. + + + The log4net configuration file can possible be specified in the application's + configuration file (either MyAppName.exe.config for a + normal application on Web.config for an ASP.NET application). + + + The following example configures log4net using a configuration file, of which the + location is stored in the application's configuration file : + + + using log4net.Config; + using System.IO; + using System.Configuration; + + ... + + DOMConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"])); + + + In the .config file, the path to the log4net can be specified like this : + + + + + + + + + + + + + Configures log4net using the specified configuration file. + + A stream to load the XML configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the log4net configuration data. + + + Note that this method will NOT close the stream parameter. + + + + + + Configures the using the specified configuration + file. + + The repository to configure. + The XML file to load the configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The log4net configuration file can possible be specified in the application's + configuration file (either MyAppName.exe.config for a + normal application on Web.config for an ASP.NET application). + + + The following example configures log4net using a configuration file, of which the + location is stored in the application's configuration file : + + + using log4net.Config; + using System.IO; + using System.Configuration; + + ... + + DOMConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"])); + + + In the .config file, the path to the log4net can be specified like this : + + + + + + + + + + + + + Configures the using the specified configuration + file. + + The repository to configure. + The stream to load the XML configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + Note that this method will NOT close the stream parameter. + + + + + + Configures log4net using the file specified, monitors the file for changes + and reloads the configuration if a change is detected. + + The XML file to load the configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The configuration file will be monitored using a + and depends on the behavior of that class. + + + For more information on how to configure log4net using + a separate configuration file, see . + + + + + + + Configures the using the file specified, + monitors the file for changes and reloads the configuration if a change + is detected. + + The repository to configure. + The XML file to load the configuration from. + + + DOMConfigurator is obsolete. Use XmlConfigurator instead of DOMConfigurator. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The configuration file will be monitored using a + and depends on the behavior of that class. + + + For more information on how to configure log4net using + a separate configuration file, see . + + + + + + + Assembly level attribute to configure the . + + + + AliasDomainAttribute is obsolete. Use AliasRepositoryAttribute instead of AliasDomainAttribute. + + + This attribute may only be used at the assembly scope and can only + be used once per assembly. + + + Use this attribute to configure the + without calling one of the + methods. + + + Nicko Cadell + Gert Driesen + + + + Assembly level attribute to configure the . + + + + This attribute may only be used at the assembly scope and can only + be used once per assembly. + + + Use this attribute to configure the + without calling one of the + methods. + + + If neither of the or + properties are set the configuration is loaded from the application's .config file. + If set the property takes priority over the + property. The property + specifies a path to a file to load the config from. The path is relative to the + application's base directory; . + The property is used as a postfix to the assembly file name. + The config file must be located in the application's base directory; . + For example in a console application setting the to + config has the same effect as not specifying the or + properties. + + + The property can be set to cause the + to watch the configuration file for changes. + + + + Log4net will only look for assembly level configuration attributes once. + When using the log4net assembly level attributes to control the configuration + of log4net you must ensure that the first call to any of the + methods is made from the assembly with the configuration + attributes. + + + If you cannot guarantee the order in which log4net calls will be made from + different assemblies you must use programmatic configuration instead, i.e. + call the method directly. + + + + Nicko Cadell + Gert Driesen + + + + Default constructor + + + + Default constructor + + + + + + Configures the for the specified assembly. + + The assembly that this attribute was defined on. + The repository to configure. + + + Configure the repository using the . + The specified must extend the + class otherwise the will not be able to + configure it. + + + The does not extend . + + + + Attempt to load configuration from the local file system + + The assembly that this attribute was defined on. + The repository to configure. + + + + Configure the specified repository using a + + The repository to configure. + the FileInfo pointing to the config file + + + + Attempt to load configuration from a URI + + The assembly that this attribute was defined on. + The repository to configure. + + + + Gets or sets the filename of the configuration file. + + + The filename of the configuration file. + + + + If specified, this is the name of the configuration file to use with + the . This file path is relative to the + application base directory (). + + + The takes priority over the . + + + + + + Gets or sets the extension of the configuration file. + + + The extension of the configuration file. + + + + If specified this is the extension for the configuration file. + The path to the config file is built by using the application + base directory (), + the assembly file name and the config file extension. + + + If the is set to MyExt then + possible config file names would be: MyConsoleApp.exe.MyExt or + MyClassLibrary.dll.MyExt. + + + The takes priority over the . + + + + + + Gets or sets a value indicating whether to watch the configuration file. + + + true if the configuration should be watched, false otherwise. + + + + If this flag is specified and set to true then the framework + will watch the configuration file and will reload the config each time + the file is modified. + + + The config file can only be watched if it is loaded from local disk. + In a No-Touch (Smart Client) deployment where the application is downloaded + from a web server the config file may not reside on the local disk + and therefore it may not be able to watch it. + + + Watching configuration is not supported on the SSCLI. + + + + + + Class to register for the log4net section of the configuration file + + + The log4net section of the configuration file needs to have a section + handler registered. This is the section handler used. It simply returns + the XML element that is the root of the section. + + + Example of registering the log4net section handler : + + + +
+ + + log4net configuration XML goes here + + + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Default constructor. + + + + + + Parses the configuration section. + + The configuration settings in a corresponding parent configuration section. + The configuration context when called from the ASP.NET configuration system. Otherwise, this parameter is reserved and is a null reference. + The for the log4net section. + The for the log4net section. + + + Returns the containing the configuration data, + + + + + + Assembly level attribute that specifies a plugin to attach to + the repository. + + + + Specifies the type of a plugin to create and attach to the + assembly's repository. The plugin type must implement the + interface. + + + Nicko Cadell + Gert Driesen + + + + Interface used to create plugins. + + + + Interface used to create a plugin. + + + Nicko Cadell + Gert Driesen + + + + Creates the plugin object. + + the new plugin instance + + + Create and return a new plugin instance. + + + + + + Initializes a new instance of the class + with the specified type. + + The type name of plugin to create. + + + Create the attribute with the plugin type specified. + + + Where possible use the constructor that takes a . + + + + + + Initializes a new instance of the class + with the specified type. + + The type of plugin to create. + + + Create the attribute with the plugin type specified. + + + + + + Creates the plugin object defined by this attribute. + + + + Creates the instance of the object as + specified by this attribute. + + + The plugin object. + + + + Returns a representation of the properties of this object. + + + + Overrides base class method to + return a representation of the properties of this object. + + + A representation of the properties of this object + + + + Gets or sets the type for the plugin. + + + The type for the plugin. + + + + The type for the plugin. + + + + + + Gets or sets the type name for the plugin. + + + The type name for the plugin. + + + + The type name for the plugin. + + + Where possible use the property instead. + + + + + + Assembly level attribute to configure the . + + + + This attribute may only be used at the assembly scope and can only + be used once per assembly. + + + Use this attribute to configure the + without calling one of the + methods. + + + Nicko Cadell + + + + Construct provider attribute with type specified + + the type of the provider to use + + + The provider specified must subclass the + class. + + + + + + Configures the SecurityContextProvider + + The assembly that this attribute was defined on. + The repository to configure. + + + Creates a provider instance from the specified. + Sets this as the default security context provider . + + + + + + Gets or sets the type of the provider to use. + + + the type of the provider to use. + + + + The provider specified must subclass the + class. + + + + + + Use this class to initialize the log4net environment using an Xml tree. + + + + Configures a using an Xml tree. + + + Nicko Cadell + Gert Driesen + + + + Private constructor + + + + + Automatically configures the log4net system based on the + application's configuration settings. + + + + Each application has a configuration file. This has the + same name as the application with '.config' appended. + This file is XML and calling this function prompts the + configurator to look in that file for a section called + log4net that contains the configuration data. + + + To use this method to configure log4net you must specify + the section + handler for the log4net configuration section. See the + for an example. + + + + + + + Automatically configures the using settings + stored in the application's configuration file. + + + + Each application has a configuration file. This has the + same name as the application with '.config' appended. + This file is XML and calling this function prompts the + configurator to look in that file for a section called + log4net that contains the configuration data. + + + To use this method to configure log4net you must specify + the section + handler for the log4net configuration section. See the + for an example. + + + The repository to configure. + + + + Configures log4net using a log4net element + + + + Loads the log4net configuration from the XML element + supplied as . + + + The element to parse. + + + + Configures the using the specified XML + element. + + + Loads the log4net configuration from the XML element + supplied as . + + The repository to configure. + The element to parse. + + + + Configures log4net using the specified configuration file. + + The XML file to load the configuration from. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the log4net configuration data. + + + The log4net configuration file can possible be specified in the application's + configuration file (either MyAppName.exe.config for a + normal application on Web.config for an ASP.NET application). + + + The first element matching <configuration> will be read as the + configuration. If this file is also a .NET .config file then you must specify + a configuration section for the log4net element otherwise .NET will + complain. Set the type for the section handler to , for example: + + +
+ + + + + The following example configures log4net using a configuration file, of which the + location is stored in the application's configuration file : + + + using log4net.Config; + using System.IO; + using System.Configuration; + + ... + + XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"])); + + + In the .config file, the path to the log4net can be specified like this : + + + + + + + + + + + + + Configures log4net using the specified configuration URI. + + A URI to load the XML configuration from. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the log4net configuration data. + + + The must support the URI scheme specified. + + + + + + Configures log4net using the specified configuration data stream. + + A stream to load the XML configuration from. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the log4net configuration data. + + + Note that this method will NOT close the stream parameter. + + + + + + Configures the using the specified configuration + file. + + The repository to configure. + The XML file to load the configuration from. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The log4net configuration file can possible be specified in the application's + configuration file (either MyAppName.exe.config for a + normal application on Web.config for an ASP.NET application). + + + The first element matching <configuration> will be read as the + configuration. If this file is also a .NET .config file then you must specify + a configuration section for the log4net element otherwise .NET will + complain. Set the type for the section handler to , for example: + + +
+ + + + + The following example configures log4net using a configuration file, of which the + location is stored in the application's configuration file : + + + using log4net.Config; + using System.IO; + using System.Configuration; + + ... + + XmlConfigurator.Configure(new FileInfo(ConfigurationSettings.AppSettings["log4net-config-file"])); + + + In the .config file, the path to the log4net can be specified like this : + + + + + + + + + + + + + Configures the using the specified configuration + URI. + + The repository to configure. + A URI to load the XML configuration from. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The must support the URI scheme specified. + + + + + + Configures the using the specified configuration + file. + + The repository to configure. + The stream to load the XML configuration from. + + + The configuration data must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + Note that this method will NOT close the stream parameter. + + + + + + Configures log4net using the file specified, monitors the file for changes + and reloads the configuration if a change is detected. + + The XML file to load the configuration from. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The configuration file will be monitored using a + and depends on the behavior of that class. + + + For more information on how to configure log4net using + a separate configuration file, see . + + + + + + + Configures the using the file specified, + monitors the file for changes and reloads the configuration if a change + is detected. + + The repository to configure. + The XML file to load the configuration from. + + + The configuration file must be valid XML. It must contain + at least one element called log4net that holds + the configuration data. + + + The configuration file will be monitored using a + and depends on the behavior of that class. + + + For more information on how to configure log4net using + a separate configuration file, see . + + + + + + + Configures the specified repository using a log4net element. + + The hierarchy to configure. + The element to parse. + + + Loads the log4net configuration from the XML element + supplied as . + + + This method is ultimately called by one of the Configure methods + to load the configuration from an . + + + + + + Class used to watch config files. + + + + Uses the to monitor + changes to a specified file. Because multiple change notifications + may be raised when the file is modified, a timer is used to + compress the notifications into a single event. The timer + waits for time before delivering + the event notification. If any further + change notifications arrive while the timer is waiting it + is reset and waits again for to + elapse. + + + + + + The default amount of time to wait after receiving notification + before reloading the config file. + + + + + Watch a specified config file used to configure a repository + + The repository to configure. + The configuration file to watch. + + + Watch a specified config file used to configure a repository + + + + + + Holds the FileInfo used to configure the XmlConfigurator + + + + + Holds the repository being configured. + + + + + The timer used to compress the notification events. + + + + + Initializes a new instance of the class. + + The repository to configure. + The configuration file to watch. + + + Initializes a new instance of the class. + + + + + + Event handler used by . + + The firing the event. + The argument indicates the file that caused the event to be fired. + + + This handler reloads the configuration from the file when the event is fired. + + + + + + Event handler used by . + + The firing the event. + The argument indicates the file that caused the event to be fired. + + + This handler reloads the configuration from the file when the event is fired. + + + + + + Called by the timer when the configuration has been updated. + + null + + + + The implementation of the interface suitable + for use with the compact framework + + + + This implementation is a simple + mapping between repository name and + object. + + + The .NET Compact Framework 1.0 does not support retrieving assembly + level attributes therefore unlike the DefaultRepositorySelector + this selector does not examine the calling assembly for attributes. + + + Nicko Cadell + + + + Interface used by the to select the . + + + + The uses a + to specify the policy for selecting the correct + to return to the caller. + + + Nicko Cadell + Gert Driesen + + + + Gets the for the specified assembly. + + The assembly to use to lookup to the + The for the assembly. + + + Gets the for the specified assembly. + + + How the association between and + is made is not defined. The implementation may choose any method for + this association. The results of this method must be repeatable, i.e. + when called again with the same arguments the result must be the + save value. + + + + + + Gets the named . + + The name to use to lookup to the . + The named + + Lookup a named . This is the repository created by + calling . + + + + + Creates a new repository for the assembly specified. + + The assembly to use to create the domain to associate with the . + The type of repository to create, must implement . + The repository created. + + + The created will be associated with the domain + specified such that a call to with the + same assembly specified will return the same repository instance. + + + How the association between and + is made is not defined. The implementation may choose any method for + this association. + + + + + + Creates a new repository with the name specified. + + The name to associate with the . + The type of repository to create, must implement . + The repository created. + + + The created will be associated with the name + specified such that a call to with the + same name will return the same repository instance. + + + + + + Test if a named repository exists + + the named repository to check + true if the repository exists + + + Test if a named repository exists. Use + to create a new repository and to retrieve + a repository. + + + + + + Gets an array of all currently defined repositories. + + + An array of the instances created by + this . + + + Gets an array of all of the repositories created by this selector. + + + + + + Event to notify that a logger repository has been created. + + + Event to notify that a logger repository has been created. + + + + Event raised when a new repository is created. + The event source will be this selector. The event args will + be a which + holds the newly created . + + + + + + Create a new repository selector + + the type of the repositories to create, must implement + + + Create an new compact repository selector. + The default type for repositories must be specified, + an appropriate value would be . + + + throw if is null + throw if does not implement + + + + Get the for the specified assembly + + not used + The default + + + The argument is not used. This selector does not create a + separate repository for each assembly. + + + As a named repository is not specified the default repository is + returned. The default repository is named log4net-default-repository. + + + + + + Get the named + + the name of the repository to lookup + The named + + + Get the named . The default + repository is log4net-default-repository. Other repositories + must be created using the . + If the named repository does not exist an exception is thrown. + + + throw if is null + throw if the does not exist + + + + Create a new repository for the assembly specified + + not used + the type of repository to create, must implement + the repository created + + + The argument is not used. This selector does not create a + separate repository for each assembly. + + + If the is null then the + default repository type specified to the constructor is used. + + + As a named repository is not specified the default repository is + returned. The default repository is named log4net-default-repository. + + + + + + Create a new repository for the repository specified + + the repository to associate with the + the type of repository to create, must implement . + If this param is null then the default repository type is used. + the repository created + + + The created will be associated with the repository + specified such that a call to with the + same repository specified will return the same repository instance. + + + If the named repository already exists an exception will be thrown. + + + If is null then the default + repository type specified to the constructor is used. + + + throw if is null + throw if the already exists + + + + Test if a named repository exists + + the named repository to check + true if the repository exists + + + Test if a named repository exists. Use + to create a new repository and to retrieve + a repository. + + + + + + Gets a list of objects + + an array of all known objects + + + Gets an array of all of the repositories created by this selector. + + + + + + Notify the registered listeners that the repository has been created + + The repository that has been created + + + Raises the LoggerRepositoryCreatedEvent + event. + + + + + + Event to notify that a logger repository has been created. + + + Event to notify that a logger repository has been created. + + + + Event raised when a new repository is created. + The event source will be this selector. The event args will + be a which + holds the newly created . + + + + + + The default implementation of the interface. + + + + Uses attributes defined on the calling assembly to determine how to + configure the hierarchy for the repository. + + + Nicko Cadell + Gert Driesen + + + + Creates a new repository selector. + + The type of the repositories to create, must implement + + + Create an new repository selector. + The default type for repositories must be specified, + an appropriate value would be . + + + is . + does not implement . + + + + Gets the for the specified assembly. + + The assembly use to lookup the . + + + The type of the created and the repository + to create can be overridden by specifying the + attribute on the . + + + The default values are to use the + implementation of the interface and to use the + as the name of the repository. + + + The created will be automatically configured using + any attributes defined on + the . + + + The for the assembly + is . + + + + Gets the for the specified repository. + + The repository to use to lookup the . + The for the specified repository. + + + Returns the named repository. If is null + a is thrown. If the repository + does not exist a is thrown. + + + Use to create a repository. + + + is . + does not exist. + + + + Create a new repository for the assembly specified + + the assembly to use to create the repository to associate with the . + The type of repository to create, must implement . + The repository created. + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + The type of the created and + the repository to create can be overridden by specifying the + attribute on the + . The default values are to use the + implementation of the + interface and to use the + as the name of the repository. + + + The created will be automatically + configured using any + attributes defined on the . + + + If a repository for the already exists + that repository will be returned. An error will not be raised and that + repository may be of a different type to that specified in . + Also the attribute on the + assembly may be used to override the repository type specified in + . + + + is . + + + + Creates a new repository for the assembly specified. + + the assembly to use to create the repository to associate with the . + The type of repository to create, must implement . + The name to assign to the created repository + Set to true to read and apply the assembly attributes + The repository created. + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + The type of the created and + the repository to create can be overridden by specifying the + attribute on the + . The default values are to use the + implementation of the + interface and to use the + as the name of the repository. + + + The created will be automatically + configured using any + attributes defined on the . + + + If a repository for the already exists + that repository will be returned. An error will not be raised and that + repository may be of a different type to that specified in . + Also the attribute on the + assembly may be used to override the repository type specified in + . + + + is . + + + + Creates a new repository for the specified repository. + + The repository to associate with the . + The type of repository to create, must implement . + If this param is then the default repository type is used. + The new repository. + + + The created will be associated with the repository + specified such that a call to with the + same repository specified will return the same repository instance. + + + is . + already exists. + + + + Test if a named repository exists + + the named repository to check + true if the repository exists + + + Test if a named repository exists. Use + to create a new repository and to retrieve + a repository. + + + + + + Gets a list of objects + + an array of all known objects + + + Gets an array of all of the repositories created by this selector. + + + + + + Aliases a repository to an existing repository. + + The repository to alias. + The repository that the repository is aliased to. + + + The repository specified will be aliased to the repository when created. + The repository must not already exist. + + + When the repository is created it must utilize the same repository type as + the repository it is aliased to, otherwise the aliasing will fail. + + + + is . + -or- + is . + + + + + Notifies the registered listeners that the repository has been created. + + The repository that has been created. + + + Raises the event. + + + + + + Gets the repository name and repository type for the specified assembly. + + The assembly that has a . + in/out param to hold the repository name to use for the assembly, caller should set this to the default value before calling. + in/out param to hold the type of the repository to create for the assembly, caller should set this to the default value before calling. + is . + + + + Configures the repository using information from the assembly. + + The assembly containing + attributes which define the configuration for the repository. + The repository to configure. + + is . + -or- + is . + + + + + Loads the attribute defined plugins on the assembly. + + The assembly that contains the attributes. + The repository to add the plugins to. + + is . + -or- + is . + + + + + Loads the attribute defined aliases on the assembly. + + The assembly that contains the attributes. + The repository to alias to. + + is . + -or- + is . + + + + + Event to notify that a logger repository has been created. + + + Event to notify that a logger repository has been created. + + + + Event raised when a new repository is created. + The event source will be this selector. The event args will + be a which + holds the newly created . + + + + + + Defined error codes that can be passed to the method. + + + + Values passed to the method. + + + Nicko Cadell + + + + A general error + + + + + Error while writing output + + + + + Failed to flush file + + + + + Failed to close file + + + + + Unable to open output file + + + + + No layout specified + + + + + Failed to parse address + + + + + Appenders may delegate their error handling to an . + + + + Error handling is a particularly tedious to get right because by + definition errors are hard to predict and to reproduce. + + + Nicko Cadell + Gert Driesen + + + + Handles the error and information about the error condition is passed as + a parameter. + + The message associated with the error. + The that was thrown when the error occurred. + The error code associated with the error. + + + Handles the error and information about the error condition is passed as + a parameter. + + + + + + Prints the error message passed as a parameter. + + The message associated with the error. + The that was thrown when the error occurred. + + + See . + + + + + + Prints the error message passed as a parameter. + + The message associated with the error. + + + See . + + + + + + Interface for objects that require fixing. + + + + Interface that indicates that the object requires fixing before it + can be taken outside the context of the appender's + method. + + + When objects that implement this interface are stored + in the context properties maps + and + are fixed + (see ) the + method will be called. + + + Nicko Cadell + + + + Get a portable version of this object + + the portable instance of this object + + + Get a portable instance object that represents the current + state of this object. The portable object can be stored + and logged from any thread with identical results. + + + + + + Interface that all loggers implement + + + + This interface supports logging events and testing if a level + is enabled for logging. + + + These methods will not throw exceptions. Note to implementor, ensure + that the implementation of these methods cannot allow an exception + to be thrown to the caller. + + + Nicko Cadell + Gert Driesen + + + + This generic form is intended to be used by wrappers. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The level of the message to be logged. + The message object to log. + the exception to log, including its stack trace. Pass null to not log an exception. + + + Generates a logging event for the specified using + the and . + + + + + + This is the most generic printing method that is intended to be used + by wrappers. + + The event being logged. + + + Logs the specified logging event through this logger. + + + + + + Checks if this logger is enabled for a given passed as parameter. + + The level to check. + + true if this logger is enabled for level, otherwise false. + + + + Test if this logger is going to log events of the specified . + + + + + + Gets the name of the logger. + + + The name of the logger. + + + + The name of this logger + + + + + + Gets the where this + Logger instance is attached to. + + + The that this logger belongs to. + + + + Gets the where this + Logger instance is attached to. + + + + + + Base interface for all wrappers + + + + Base interface for all wrappers. + + + All wrappers must implement this interface. + + + Nicko Cadell + + + + Get the implementation behind this wrapper object. + + + The object that in implementing this object. + + + + The object that in implementing this + object. The Logger object may not + be the same object as this object because of logger decorators. + This gets the actual underlying objects that is used to process + the log events. + + + + + + Delegate used to handle logger repository creation event notifications + + The which created the repository. + The event args + that holds the instance that has been created. + + + Delegate used to handle logger repository creation event notifications. + + + + + + Provides data for the event. + + + + A + event is raised every time a is created. + + + + + + The created + + + + + Construct instance using specified + + the that has been created + + + Construct instance using specified + + + + + + The that has been created + + + The that has been created + + + + The that has been created + + + + + + Test if an triggers an action + + + + Implementations of this interface allow certain appenders to decide + when to perform an appender specific action. + + + The action or behavior triggered is defined by the implementation. + + + Nicko Cadell + + + + Test if this event triggers the action + + The event to check + true if this event triggers the action, otherwise false + + + Return true if this event triggers the action + + + + + + Defines the default set of levels recognized by the system. + + + + Each has an associated . + + + Levels have a numeric that defines the relative + ordering between levels. Two Levels with the same + are deemed to be equivalent. + + + The levels that are recognized by log4net are set for each + and each repository can have different levels defined. The levels are stored + in the on the repository. Levels are + looked up by name from the . + + + When logging at level INFO the actual level used is not but + the value of LoggerRepository.LevelMap["INFO"]. The default value for this is + , but this can be changed by reconfiguring the level map. + + + Each level has a in addition to its . The + is the string that is written into the output log. By default + the display name is the same as the level name, but this can be used to alias levels + or to localize the log output. + + + Some of the predefined levels recognized by the system are: + + + + . + + + . + + + . + + + . + + + . + + + . + + + . + + + + Nicko Cadell + Gert Driesen + + + + Constructor + + Integer value for this level, higher values represent more severe levels. + The string name of this level. + The display name for this level. This may be localized or otherwise different from the name + + + Initializes a new instance of the class with + the specified level name and value. + + + + + + Constructor + + Integer value for this level, higher values represent more severe levels. + The string name of this level. + + + Initializes a new instance of the class with + the specified level name and value. + + + + + + Returns the representation of the current + . + + + A representation of the current . + + + + Returns the level . + + + + + + Compares levels. + + The object to compare against. + true if the objects are equal. + + + Compares the levels of instances, and + defers to base class if the target object is not a + instance. + + + + + + Returns a hash code + + A hash code for the current . + + + Returns a hash code suitable for use in hashing algorithms and data + structures like a hash table. + + + Returns the hash code of the level . + + + + + + Compares this instance to a specified object and returns an + indication of their relative values. + + A instance or to compare with this instance. + + A 32-bit signed integer that indicates the relative order of the + values compared. The return value has these meanings: + + + Value + Meaning + + + Less than zero + This instance is less than . + + + Zero + This instance is equal to . + + + Greater than zero + + This instance is greater than . + -or- + is . + + + + + + + must be an instance of + or ; otherwise, an exception is thrown. + + + is not a . + + + + Returns a value indicating whether a specified + is greater than another specified . + + A + A + + true if is greater than + ; otherwise, false. + + + + Compares two levels. + + + + + + Returns a value indicating whether a specified + is less than another specified . + + A + A + + true if is less than + ; otherwise, false. + + + + Compares two levels. + + + + + + Returns a value indicating whether a specified + is greater than or equal to another specified . + + A + A + + true if is greater than or equal to + ; otherwise, false. + + + + Compares two levels. + + + + + + Returns a value indicating whether a specified + is less than or equal to another specified . + + A + A + + true if is less than or equal to + ; otherwise, false. + + + + Compares two levels. + + + + + + Returns a value indicating whether two specified + objects have the same value. + + A or . + A or . + + true if the value of is the same as the + value of ; otherwise, false. + + + + Compares two levels. + + + + + + Returns a value indicating whether two specified + objects have different values. + + A or . + A or . + + true if the value of is different from + the value of ; otherwise, false. + + + + Compares two levels. + + + + + + Compares two specified instances. + + The first to compare. + The second to compare. + + A 32-bit signed integer that indicates the relative order of the + two values compared. The return value has these meanings: + + + Value + Meaning + + + Less than zero + is less than . + + + Zero + is equal to . + + + Greater than zero + is greater than . + + + + + + Compares two levels. + + + + + + The level designates a higher level than all the rest. + + + + + The level designates very severe error events. + System unusable, emergencies. + + + + + The level designates very severe error events + that will presumably lead the application to abort. + + + + + The level designates very severe error events. + Take immediate action, alerts. + + + + + The level designates very severe error events. + Critical condition, critical. + + + + + The level designates very severe error events. + + + + + The level designates error events that might + still allow the application to continue running. + + + + + The level designates potentially harmful + situations. + + + + + The level designates informational messages + that highlight the progress of the application at the highest level. + + + + + The level designates informational messages that + highlight the progress of the application at coarse-grained level. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates fine-grained informational + events that are most useful to debug an application. + + + + + The level designates the lowest level possible. + + + + + Gets the name of this level. + + + The name of this level. + + + + Gets the name of this level. + + + + + + Gets the value of this level. + + + The value of this level. + + + + Gets the value of this level. + + + + + + Gets the display name of this level. + + + The display name of this level. + + + + Gets the display name of this level. + + + + + + A strongly-typed collection of objects. + + Nicko Cadell + + + + Creates a read-only wrapper for a LevelCollection instance. + + list to create a readonly wrapper arround + + A LevelCollection wrapper that is read-only. + + + + + Initializes a new instance of the LevelCollection class + that is empty and has the default initial capacity. + + + + + Initializes a new instance of the LevelCollection class + that has the specified initial capacity. + + + The number of elements that the new LevelCollection is initially capable of storing. + + + + + Initializes a new instance of the LevelCollection class + that contains elements copied from the specified LevelCollection. + + The LevelCollection whose elements are copied to the new collection. + + + + Initializes a new instance of the LevelCollection class + that contains elements copied from the specified array. + + The array whose elements are copied to the new list. + + + + Initializes a new instance of the LevelCollection class + that contains elements copied from the specified collection. + + The collection whose elements are copied to the new list. + + + + Allow subclasses to avoid our default constructors + + + + + + Copies the entire LevelCollection to a one-dimensional + array. + + The one-dimensional array to copy to. + + + + Copies the entire LevelCollection to a one-dimensional + array, starting at the specified index of the target array. + + The one-dimensional array to copy to. + The zero-based index in at which copying begins. + + + + Adds a to the end of the LevelCollection. + + The to be added to the end of the LevelCollection. + The index at which the value has been added. + + + + Removes all elements from the LevelCollection. + + + + + Creates a shallow copy of the . + + A new with a shallow copy of the collection data. + + + + Determines whether a given is in the LevelCollection. + + The to check for. + true if is found in the LevelCollection; otherwise, false. + + + + Returns the zero-based index of the first occurrence of a + in the LevelCollection. + + The to locate in the LevelCollection. + + The zero-based index of the first occurrence of + in the entire LevelCollection, if found; otherwise, -1. + + + + + Inserts an element into the LevelCollection at the specified index. + + The zero-based index at which should be inserted. + The to insert. + + is less than zero + -or- + is equal to or greater than . + + + + + Removes the first occurrence of a specific from the LevelCollection. + + The to remove from the LevelCollection. + + The specified was not found in the LevelCollection. + + + + + Removes the element at the specified index of the LevelCollection. + + The zero-based index of the element to remove. + + is less than zero + -or- + is equal to or greater than . + + + + + Returns an enumerator that can iterate through the LevelCollection. + + An for the entire LevelCollection. + + + + Adds the elements of another LevelCollection to the current LevelCollection. + + The LevelCollection whose elements should be added to the end of the current LevelCollection. + The new of the LevelCollection. + + + + Adds the elements of a array to the current LevelCollection. + + The array whose elements should be added to the end of the LevelCollection. + The new of the LevelCollection. + + + + Adds the elements of a collection to the current LevelCollection. + + The collection whose elements should be added to the end of the LevelCollection. + The new of the LevelCollection. + + + + Sets the capacity to the actual number of elements. + + + + + is less than zero + -or- + is equal to or greater than . + + + + + is less than zero + -or- + is equal to or greater than . + + + + + Gets the number of elements actually contained in the LevelCollection. + + + + + Gets a value indicating whether access to the collection is synchronized (thread-safe). + + true if access to the ICollection is synchronized (thread-safe); otherwise, false. + + + + Gets an object that can be used to synchronize access to the collection. + + + + + Gets or sets the at the specified index. + + The zero-based index of the element to get or set. + + is less than zero + -or- + is equal to or greater than . + + + + + Gets a value indicating whether the collection has a fixed size. + + true if the collection has a fixed size; otherwise, false. The default is false + + + + Gets a value indicating whether the IList is read-only. + + true if the collection is read-only; otherwise, false. The default is false + + + + Gets or sets the number of elements the LevelCollection can contain. + + + + + Supports type-safe iteration over a . + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + + + Type visible only to our subclasses + Used to access protected constructor + + + + + A value + + + + + Supports simple iteration over a . + + + + + Initializes a new instance of the Enumerator class. + + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + + + An evaluator that triggers at a threshold level + + + + This evaluator will trigger if the level of the event + passed to + is equal to or greater than the + level. + + + Nicko Cadell + + + + The threshold for triggering + + + + + Create a new evaluator using the threshold. + + + + Create a new evaluator using the threshold. + + + This evaluator will trigger if the level of the event + passed to + is equal to or greater than the + level. + + + + + + Create a new evaluator using the specified threshold. + + the threshold to trigger at + + + Create a new evaluator using the specified threshold. + + + This evaluator will trigger if the level of the event + passed to + is equal to or greater than the + level. + + + + + + Is this the triggering event? + + The event to check + This method returns true, if the event level + is equal or higher than the . + Otherwise it returns false + + + This evaluator will trigger if the level of the event + passed to + is equal to or greater than the + level. + + + + + + the threshold to trigger at + + + The that will cause this evaluator to trigger + + + + This evaluator will trigger if the level of the event + passed to + is equal to or greater than the + level. + + + + + + Mapping between string name and Level object + + + + Mapping between string name and object. + This mapping is held separately for each . + The level name is case insensitive. + + + Nicko Cadell + + + + Mapping from level name to Level object. The + level name is case insensitive + + + + + Construct the level map + + + + Construct the level map. + + + + + + Clear the internal maps of all levels + + + + Clear the internal maps of all levels + + + + + + Create a new Level and add it to the map + + the string to display for the Level + the level value to give to the Level + + + Create a new Level and add it to the map + + + + + + + Create a new Level and add it to the map + + the string to display for the Level + the level value to give to the Level + the display name to give to the Level + + + Create a new Level and add it to the map + + + + + + Add a Level to the map + + the Level to add + + + Add a Level to the map + + + + + + Lookup a named level from the map + + the name of the level to lookup is taken from this level. + If the level is not set on the map then this level is added + the level in the map with the name specified + + + Lookup a named level from the map. The name of the level to lookup is taken + from the property of the + argument. + + + If no level with the specified name is found then the + argument is added to the level map + and returned. + + + + + + Lookup a by name + + The name of the Level to lookup + a Level from the map with the name specified + + + Returns the from the + map with the name specified. If the no level is + found then null is returned. + + + + + + Return all possible levels as a list of Level objects. + + all possible levels as a list of Level objects + + + Return all possible levels as a list of Level objects. + + + + + + The internal representation of caller location information. + + + + This class uses the System.Diagnostics.StackTrace class to generate + a call stack. The caller's information is then extracted from this stack. + + + The System.Diagnostics.StackTrace class is not supported on the + .NET Compact Framework 1.0 therefore caller location information is not + available on that framework. + + + The System.Diagnostics.StackTrace class has this to say about Release builds: + + + "StackTrace information will be most informative with Debug build configurations. + By default, Debug builds include debug symbols, while Release builds do not. The + debug symbols contain most of the file, method name, line number, and column + information used in constructing StackFrame and StackTrace objects. StackTrace + might not report as many method calls as expected, due to code transformations + that occur during optimization." + + + This means that in a Release build the caller information may be incomplete or may + not exist at all! Therefore caller location information cannot be relied upon in a Release build. + + + Nicko Cadell + Gert Driesen + + + + When location information is not available the constant + NA is returned. Current value of this string + constant is ?. + + + + + Constructor + + The declaring type of the method that is + the stack boundary into the logging system for this call. + + + Initializes a new instance of the + class based on the current thread. + + + + + + Constructor + + The fully qualified class name. + The method name. + The file name. + The line number of the method within the file. + + + Initializes a new instance of the + class with the specified data. + + + + + + Gets the fully qualified class name of the caller making the logging + request. + + + The fully qualified class name of the caller making the logging + request. + + + + Gets the fully qualified class name of the caller making the logging + request. + + + + + + Gets the file name of the caller. + + + The file name of the caller. + + + + Gets the file name of the caller. + + + + + + Gets the line number of the caller. + + + The line number of the caller. + + + + Gets the line number of the caller. + + + + + + Gets the method name of the caller. + + + The method name of the caller. + + + + Gets the method name of the caller. + + + + + + Gets all available caller information + + + All available caller information, in the format + fully.qualified.classname.of.caller.methodName(Filename:line) + + + + Gets all available caller information, in the format + fully.qualified.classname.of.caller.methodName(Filename:line) + + + + + + Static manager that controls the creation of repositories + + + + Static manager that controls the creation of repositories + + + This class is used by the wrapper managers (e.g. ) + to provide access to the objects. + + + This manager also holds the that is used to + lookup and create repositories. The selector can be set either programmatically using + the property, or by setting the log4net.RepositorySelector + AppSetting in the applications config file to the fully qualified type name of the + selector to use. + + + Nicko Cadell + Gert Driesen + + + + Private constructor to prevent instances. Only static methods should be used. + + + + Private constructor to prevent instances. Only static methods should be used. + + + + + + Hook the shutdown event + + + + On the full .NET runtime, the static constructor hooks up the + AppDomain.ProcessExit and AppDomain.DomainUnload> events. + These are used to shutdown the log4net system as the application exits. + + + + + + Register for ProcessExit and DomainUnload events on the AppDomain + + + + This needs to be in a separate method because the events make + a LinkDemand for the ControlAppDomain SecurityPermission. Because + this is a LinkDemand it is demanded at JIT time. Therefore we cannot + catch the exception in the method itself, we have to catch it in the + caller. + + + + + + Return the default instance. + + the repository to lookup in + Return the default instance + + + Gets the for the repository specified + by the argument. + + + + + + Returns the default instance. + + The assembly to use to lookup the repository. + The default instance. + + + + Return the default instance. + + the repository to lookup in + Return the default instance + + + Gets the for the repository specified + by the argument. + + + + + + Returns the default instance. + + The assembly to use to lookup the repository. + The default instance. + + + Returns the default instance. + + + + + + Returns the named logger if it exists. + + The repository to lookup in. + The fully qualified logger name to look for. + + The logger found, or null if the named logger does not exist in the + specified repository. + + + + If the named logger exists (in the specified repository) then it + returns a reference to the logger, otherwise it returns + null. + + + + + + Returns the named logger if it exists. + + The assembly to use to lookup the repository. + The fully qualified logger name to look for. + + The logger found, or null if the named logger does not exist in the + specified assembly's repository. + + + + If the named logger exists (in the specified assembly's repository) then it + returns a reference to the logger, otherwise it returns + null. + + + + + + Returns all the currently defined loggers in the specified repository. + + The repository to lookup in. + All the defined loggers. + + + The root logger is not included in the returned array. + + + + + + Returns all the currently defined loggers in the specified assembly's repository. + + The assembly to use to lookup the repository. + All the defined loggers. + + + The root logger is not included in the returned array. + + + + + + Retrieves or creates a named logger. + + The repository to lookup in. + The name of the logger to retrieve. + The logger with the name specified. + + + Retrieves a logger named as the + parameter. If the named logger already exists, then the + existing instance will be returned. Otherwise, a new instance is + created. + + + By default, loggers do not have a set level but inherit + it from the hierarchy. This is one of the central features of + log4net. + + + + + + Retrieves or creates a named logger. + + The assembly to use to lookup the repository. + The name of the logger to retrieve. + The logger with the name specified. + + + Retrieves a logger named as the + parameter. If the named logger already exists, then the + existing instance will be returned. Otherwise, a new instance is + created. + + + By default, loggers do not have a set level but inherit + it from the hierarchy. This is one of the central features of + log4net. + + + + + + Shorthand for . + + The repository to lookup in. + The of which the fullname will be used as the name of the logger to retrieve. + The logger with the name specified. + + + Gets the logger for the fully qualified name of the type specified. + + + + + + Shorthand for . + + the assembly to use to lookup the repository + The of which the fullname will be used as the name of the logger to retrieve. + The logger with the name specified. + + + Gets the logger for the fully qualified name of the type specified. + + + + + + Shuts down the log4net system. + + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in all the + default repositories. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Shuts down the repository for the repository specified. + + The repository to shutdown. + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in the + repository for the specified. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Shuts down the repository for the repository specified. + + The assembly to use to lookup the repository. + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in the + repository for the repository. The repository is looked up using + the specified. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Resets all values contained in this repository instance to their defaults. + + The repository to reset. + + + Resets all values contained in the repository instance to their + defaults. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set its default "off" value. + + + + + + Resets all values contained in this repository instance to their defaults. + + The assembly to use to lookup the repository to reset. + + + Resets all values contained in the repository instance to their + defaults. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set its default "off" value. + + + + + + Creates a repository with the specified name. + + The name of the repository, this must be unique amongst repositories. + The created for the repository. + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + Creates the default type of which is a + object. + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The specified repository already exists. + + + + Creates a repository with the specified name. + + The name of the repository, this must be unique amongst repositories. + The created for the repository. + + + Creates the default type of which is a + object. + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The specified repository already exists. + + + + Creates a repository with the specified name and repository type. + + The name of the repository, this must be unique to the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + The name must be unique. Repositories cannot be redefined. + An Exception will be thrown if the repository already exists. + + + The specified repository already exists. + + + + Creates a repository with the specified name and repository type. + + The name of the repository, this must be unique to the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + The name must be unique. Repositories cannot be redefined. + An Exception will be thrown if the repository already exists. + + + The specified repository already exists. + + + + Creates a repository for the specified assembly and repository type. + + The assembly to use to get the name of the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + + + + Creates a repository for the specified assembly and repository type. + + The assembly to use to get the name of the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + + + + Gets an array of all currently defined repositories. + + An array of all the known objects. + + + Gets an array of all currently defined repositories. + + + + + + Internal method to get pertinent version info. + + A string of version info. + + + + Called when the event fires + + the that is exiting + null + + + Called when the event fires. + + + When the event is triggered the log4net system is . + + + + + + Called when the event fires + + the that is exiting + null + + + Called when the event fires. + + + When the event is triggered the log4net system is . + + + + + + Initialize the default repository selector + + + + + Gets or sets the repository selector used by the . + + + The repository selector used by the . + + + + The repository selector () is used by + the to create and select repositories + (). + + + The caller to supplies either a string name + or an assembly (if not supplied the assembly is inferred using + ). + + + This context is used by the selector to lookup a specific repository. + + + For the full .NET Framework, the default repository is DefaultRepositorySelector; + for the .NET Compact Framework CompactRepositorySelector is the default + repository. + + + + + + Implementation of the interface. + + + + This class should be used as the base for all wrapper implementations. + + + Nicko Cadell + Gert Driesen + + + + Constructs a new wrapper for the specified logger. + + The logger to wrap. + + + Constructs a new wrapper for the specified logger. + + + + + + The logger that this object is wrapping + + + + + Gets the implementation behind this wrapper object. + + + The object that this object is implementing. + + + + The Logger object may not be the same object as this object + because of logger decorators. + + + This gets the actual underlying objects that is used to process + the log events. + + + + + + Portable data structure used by + + + + Portable data structure used by + + + Nicko Cadell + + + + The logger name. + + + + The logger name. + + + + + + Level of logging event. + + + + Level of logging event. Level cannot be Serializable + because it is a flyweight. Due to its special serialization it + cannot be declared final either. + + + + + + The application supplied message. + + + + The application supplied message of logging event. + + + + + + The name of thread + + + + The name of thread in which this logging event was generated + + + + + + The time the event was logged + + + + The TimeStamp is stored in the local time zone for this computer. + + + + + + Location information for the caller. + + + + Location information for the caller. + + + + + + String representation of the user + + + + String representation of the user's windows name, + like DOMAIN\username + + + + + + String representation of the identity. + + + + String representation of the current thread's principal identity. + + + + + + The string representation of the exception + + + + The string representation of the exception + + + + + + String representation of the AppDomain. + + + + String representation of the AppDomain. + + + + + + Additional event specific properties + + + + A logger or an appender may attach additional + properties to specific events. These properties + have a string key and an object value. + + + + + + Flags passed to the property + + + + Flags passed to the property + + + Nicko Cadell + + + + Fix the MDC + + + + + Fix the NDC + + + + + Fix the rendered message + + + + + Fix the thread name + + + + + Fix the callers location information + + + CAUTION: Very slow to generate + + + + + Fix the callers windows user name + + + CAUTION: Slow to generate + + + + + Fix the domain friendly name + + + + + Fix the callers principal name + + + CAUTION: May be slow to generate + + + + + Fix the exception text + + + + + Fix the event properties + + + + + No fields fixed + + + + + All fields fixed + + + + + Partial fields fixed + + + + This set of partial fields gives good performance. The following fields are fixed: + + + + + + + + + + + + + The internal representation of logging events. + + + + When an affirmative decision is made to log then a + instance is created. This instance + is passed around to the different log4net components. + + + This class is of concern to those wishing to extend log4net. + + + Some of the values in instances of + are considered volatile, that is the values are correct at the + time the event is delivered to appenders, but will not be consistent + at any time afterwards. If an event is to be stored and then processed + at a later time these volatile values must be fixed by calling + . There is a performance penalty + for incurred by calling but it + is essential to maintaining data consistency. + + + Nicko Cadell + Gert Driesen + Douglas de la Torre + Daniel Cazzulino + + + + The key into the Properties map for the host name value. + + + + + The key into the Properties map for the thread identity value. + + + + + The key into the Properties map for the user name value. + + + + + Initializes a new instance of the class + from the supplied parameters. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The repository this event is logged in. + The name of the logger of this event. + The level of this event. + The message of this event. + The exception for this event. + + + Except , and , + all fields of LoggingEvent are filled when actually needed. Call + to cache all data locally + to prevent inconsistencies. + + This method is called by the log4net framework + to create a logging event. + + + + + + Initializes a new instance of the class + using specific data. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The repository this event is logged in. + Data used to initialize the logging event. + The fields in the struct that have already been fixed. + + + This constructor is provided to allow a + to be created independently of the log4net framework. This can + be useful if you require a custom serialization scheme. + + + Use the method to obtain an + instance of the class. + + + The parameter should be used to specify which fields in the + struct have been preset. Fields not specified in the + will be captured from the environment if requested or fixed. + + + + + + Initializes a new instance of the class + using specific data. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The repository this event is logged in. + Data used to initialize the logging event. + + + This constructor is provided to allow a + to be created independently of the log4net framework. This can + be useful if you require a custom serialization scheme. + + + Use the method to obtain an + instance of the class. + + + This constructor sets this objects flags to , + this assumes that all the data relating to this event is passed in via the + parameter and no other data should be captured from the environment. + + + + + + Initializes a new instance of the class + using specific data. + + Data used to initialize the logging event. + + + This constructor is provided to allow a + to be created independently of the log4net framework. This can + be useful if you require a custom serialization scheme. + + + Use the method to obtain an + instance of the class. + + + This constructor sets this objects flags to , + this assumes that all the data relating to this event is passed in via the + parameter and no other data should be captured from the environment. + + + + + + Serialization constructor + + The that holds the serialized object data. + The that contains contextual information about the source or destination. + + + Initializes a new instance of the class + with serialized data. + + + + + + Ensure that the repository is set. + + the value for the repository + + + + Write the rendered message to a TextWriter + + the writer to write the message to + + + Unlike the property this method + does store the message data in the internal cache. Therefore + if called only once this method should be faster than the + property, however if the message is + to be accessed multiple times then the property will be more efficient. + + + + + + Serializes this object into the provided. + + The to populate with data. + The destination for this serialization. + + + The data in this event must be fixed before it can be serialized. + + + The method must be called during the + method call if this event + is to be used outside that method. + + + + + + Gets the portable data for this . + + The for this event. + + + A new can be constructed using a + instance. + + + Does a fix of the data + in the logging event before returning the event data. + + + + + + Gets the portable data for this . + + The set of data to ensure is fixed in the LoggingEventData + The for this event. + + + A new can be constructed using a + instance. + + + + + + Returns this event's exception's rendered using the + . + + + This event's exception's rendered using the . + + + + Obsolete. Use instead. + + + + + + Returns this event's exception's rendered using the + . + + + This event's exception's rendered using the . + + + + Returns this event's exception's rendered using the + . + + + + + + Fix instance fields that hold volatile data. + + + + Some of the values in instances of + are considered volatile, that is the values are correct at the + time the event is delivered to appenders, but will not be consistent + at any time afterwards. If an event is to be stored and then processed + at a later time these volatile values must be fixed by calling + . There is a performance penalty + incurred by calling but it + is essential to maintaining data consistency. + + + Calling is equivalent to + calling passing the parameter + false. + + + See for more + information. + + + + + + Fixes instance fields that hold volatile data. + + Set to true to not fix data that takes a long time to fix. + + + Some of the values in instances of + are considered volatile, that is the values are correct at the + time the event is delivered to appenders, but will not be consistent + at any time afterwards. If an event is to be stored and then processed + at a later time these volatile values must be fixed by calling + . There is a performance penalty + for incurred by calling but it + is essential to maintaining data consistency. + + + The param controls the data that + is fixed. Some of the data that can be fixed takes a long time to + generate, therefore if you do not require those settings to be fixed + they can be ignored by setting the param + to true. This setting will ignore the + and settings. + + + Set to false to ensure that all + settings are fixed. + + + + + + Fix the fields specified by the parameter + + the fields to fix + + + Only fields specified in the will be fixed. + Fields will not be fixed if they have previously been fixed. + It is not possible to 'unfix' a field. + + + + + + Lookup a composite property in this event + + the key for the property to lookup + the value for the property + + + This event has composite properties that combine together properties from + several different contexts in the following order: + + + this events properties + + This event has that can be set. These + properties are specific to this event only. + + + + the thread properties + + The that are set on the current + thread. These properties are shared by all events logged on this thread. + + + + the global properties + + The that are set globally. These + properties are shared by all the threads in the AppDomain. + + + + + + + + + Get all the composite properties in this event + + the containing all the properties + + + See for details of the composite properties + stored by the event. + + + This method returns a single containing all the + properties defined for this event. + + + + + + The internal logging event data. + + + + + The internal logging event data. + + + + + The internal logging event data. + + + + + The fully qualified Type of the calling + logger class in the stack frame (i.e. the declaring type of the method). + + + + + The application supplied message of logging event. + + + + + The exception that was thrown. + + + This is not serialized. The string representation + is serialized instead. + + + + + The repository that generated the logging event + + + This is not serialized. + + + + + The fix state for this event + + + These flags indicate which fields have been fixed. + Not serialized. + + + + + Indicated that the internal cache is updateable (ie not fixed) + + + This is a seperate flag to m_fixFlags as it allows incrementel fixing and simpler + changes in the caching strategy. + + + + + Gets the time when the current process started. + + + This is the time when this process started. + + + + The TimeStamp is stored in the local time zone for this computer. + + + Tries to get the start time for the current process. + Failing that it returns the time of the first call to + this property. + + + Note that AppDomains may be loaded and unloaded within the + same process without the process terminating and therefore + without the process start time being reset. + + + + + + Gets the of the logging event. + + + The of the logging event. + + + + Gets the of the logging event. + + + + + + Gets the time of the logging event. + + + The time of the logging event. + + + + The TimeStamp is stored in the local time zone for this computer. + + + + + + Gets the name of the logger that logged the event. + + + The name of the logger that logged the event. + + + + Gets the name of the logger that logged the event. + + + + + + Gets the location information for this logging event. + + + The location information for this logging event. + + + + The collected information is cached for future use. + + + See the class for more information on + supported frameworks and the different behavior in Debug and + Release builds. + + + + + + Gets the message object used to initialize this event. + + + The message object used to initialize this event. + + + + Gets the message object used to initialize this event. + Note that this event may not have a valid message object. + If the event is serialized the message object will not + be transferred. To get the text of the message the + property must be used + not this property. + + + If there is no defined message object for this event then + null will be returned. + + + + + + Gets the exception object used to initialize this event. + + + The exception object used to initialize this event. + + + + Gets the exception object used to initialize this event. + Note that this event may not have a valid exception object. + If the event is serialized the exception object will not + be transferred. To get the text of the exception the + method must be used + not this property. + + + If there is no defined exception object for this event then + null will be returned. + + + + + + The that this event was created in. + + + + The that this event was created in. + + + + + + Gets the message, rendered through the . + + + The message rendered through the . + + + + The collected information is cached for future use. + + + + + + Gets the name of the current thread. + + + The name of the current thread, or the thread ID when + the name is not available. + + + + The collected information is cached for future use. + + + + + + Gets the name of the current user. + + + The name of the current user, or NOT AVAILABLE when the + underlying runtime has no support for retrieving the name of the + current user. + + + + Calls WindowsIdentity.GetCurrent().Name to get the name of + the current windows user. + + + To improve performance, we could cache the string representation of + the name, and reuse that as long as the identity stayed constant. + Once the identity changed, we would need to re-assign and re-render + the string. + + + However, the WindowsIdentity.GetCurrent() call seems to + return different objects every time, so the current implementation + doesn't do this type of caching. + + + Timing for these operations: + + + + Method + Results + + + WindowsIdentity.GetCurrent() + 10000 loops, 00:00:00.2031250 seconds + + + WindowsIdentity.GetCurrent().Name + 10000 loops, 00:00:08.0468750 seconds + + + + This means we could speed things up almost 40 times by caching the + value of the WindowsIdentity.GetCurrent().Name property, since + this takes (8.04-0.20) = 7.84375 seconds. + + + + + + Gets the identity of the current thread principal. + + + The string name of the identity of the current thread principal. + + + + Calls System.Threading.Thread.CurrentPrincipal.Identity.Name to get + the name of the current thread principal. + + + + + + Gets the AppDomain friendly name. + + + The AppDomain friendly name. + + + + Gets the AppDomain friendly name. + + + + + + Additional event specific properties. + + + Additional event specific properties. + + + + A logger or an appender may attach additional + properties to specific events. These properties + have a string key and an object value. + + + This property is for events that have been added directly to + this event. The aggregate properties (which include these + event properties) can be retrieved using + and . + + + Once the properties have been fixed this property + returns the combined cached properties. This ensures that updates to + this property are always reflected in the underlying storage. When + returning the combined properties there may be more keys in the + Dictionary than expected. + + + + + + The fixed fields in this event + + + The set of fields that are fixed in this event + + + + Fields will not be fixed if they have previously been fixed. + It is not possible to 'unfix' a field. + + + + + + Implementation of wrapper interface. + + + + This implementation of the interface + forwards to the held by the base class. + + + This logger has methods to allow the caller to log at the following + levels: + + + + DEBUG + + The and methods log messages + at the DEBUG level. That is the level with that name defined in the + repositories . The default value + for this level is . The + property tests if this level is enabled for logging. + + + + INFO + + The and methods log messages + at the INFO level. That is the level with that name defined in the + repositories . The default value + for this level is . The + property tests if this level is enabled for logging. + + + + WARN + + The and methods log messages + at the WARN level. That is the level with that name defined in the + repositories . The default value + for this level is . The + property tests if this level is enabled for logging. + + + + ERROR + + The and methods log messages + at the ERROR level. That is the level with that name defined in the + repositories . The default value + for this level is . The + property tests if this level is enabled for logging. + + + + FATAL + + The and methods log messages + at the FATAL level. That is the level with that name defined in the + repositories . The default value + for this level is . The + property tests if this level is enabled for logging. + + + + + The values for these levels and their semantic meanings can be changed by + configuring the for the repository. + + + Nicko Cadell + Gert Driesen + + + + The ILog interface is use by application to log messages into + the log4net framework. + + + + Use the to obtain logger instances + that implement this interface. The + static method is used to get logger instances. + + + This class contains methods for logging at different levels and also + has properties for determining if those logging levels are + enabled in the current configuration. + + + This interface can be implemented in different ways. This documentation + specifies reasonable behavior that a caller can expect from the actual + implementation, however different implementations reserve the right to + do things differently. + + + Simple example of logging messages + + ILog log = LogManager.GetLogger("application-log"); + + log.Info("Application Start"); + log.Debug("This is a debug message"); + + if (log.IsDebugEnabled) + { + log.Debug("This is another debug message"); + } + + + + + Nicko Cadell + Gert Driesen + + + Log a message object with the level. + + Log a message object with the level. + + The message object to log. + + + This method first checks if this logger is DEBUG + enabled by comparing the level of this logger with the + level. If this logger is + DEBUG enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of + the additivity flag. + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + + + + + + Log a message object with the level including + the stack trace of the passed + as a parameter. + + The message object to log. + The exception to log, including its stack trace. + + + See the form for more detailed information. + + + + + + + Log a formatted string with the level. + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + Log a message object with the level. + + Logs a message object with the level. + + + + This method first checks if this logger is INFO + enabled by comparing the level of this logger with the + level. If this logger is + INFO enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of the + additivity flag. + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + The message object to log. + + + + + + Logs a message object with the INFO level including + the stack trace of the passed + as a parameter. + + The message object to log. + The exception to log, including its stack trace. + + + See the form for more detailed information. + + + + + + + Log a formatted message string with the level. + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + Log a message object with the level. + + Log a message object with the level. + + + + This method first checks if this logger is WARN + enabled by comparing the level of this logger with the + level. If this logger is + WARN enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of the + additivity flag. + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + The message object to log. + + + + + + Log a message object with the level including + the stack trace of the passed + as a parameter. + + The message object to log. + The exception to log, including its stack trace. + + + See the form for more detailed information. + + + + + + + Log a formatted message string with the level. + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + Log a message object with the level. + + Logs a message object with the level. + + The message object to log. + + + This method first checks if this logger is ERROR + enabled by comparing the level of this logger with the + level. If this logger is + ERROR enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of the + additivity flag. + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + + + + + + Log a message object with the level including + the stack trace of the passed + as a parameter. + + The message object to log. + The exception to log, including its stack trace. + + + See the form for more detailed information. + + + + + + + Log a formatted message string with the level. + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + Log a message object with the level. + + Log a message object with the level. + + + + This method first checks if this logger is FATAL + enabled by comparing the level of this logger with the + level. If this logger is + FATAL enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of the + additivity flag. + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + The message object to log. + + + + + + Log a message object with the level including + the stack trace of the passed + as a parameter. + + The message object to log. + The exception to log, including its stack trace. + + + See the form for more detailed information. + + + + + + + Log a formatted message string with the level. + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Logs a formatted message string with the level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the String.Format method. See + for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + + + Checks if this logger is enabled for the level. + + + true if this logger is enabled for events, false otherwise. + + + + This function is intended to lessen the computational cost of + disabled log debug statements. + + For some ILog interface log, when you write: + + log.Debug("This is entry number: " + i ); + + + You incur the cost constructing the message, string construction and concatenation in + this case, regardless of whether the message is logged or not. + + + If you are worried about speed (who isn't), then you should write: + + + if (log.IsDebugEnabled) + { + log.Debug("This is entry number: " + i ); + } + + + This way you will not incur the cost of parameter + construction if debugging is disabled for log. On + the other hand, if the log is debug enabled, you + will incur the cost of evaluating whether the logger is debug + enabled twice. Once in and once in + the . This is an insignificant overhead + since evaluating a logger takes about 1% of the time it + takes to actually log. This is the preferred style of logging. + + Alternatively if your logger is available statically then the is debug + enabled state can be stored in a static variable like this: + + + private static readonly bool isDebugEnabled = log.IsDebugEnabled; + + + Then when you come to log you can write: + + + if (isDebugEnabled) + { + log.Debug("This is entry number: " + i ); + } + + + This way the debug enabled state is only queried once + when the class is loaded. Using a private static readonly + variable is the most efficient because it is a run time constant + and can be heavily optimized by the JIT compiler. + + + Of course if you use a static readonly variable to + hold the enabled state of the logger then you cannot + change the enabled state at runtime to vary the logging + that is produced. You have to decide if you need absolute + speed or runtime flexibility. + + + + + + + + Checks if this logger is enabled for the level. + + + true if this logger is enabled for events, false otherwise. + + + For more information see . + + + + + + + + Checks if this logger is enabled for the level. + + + true if this logger is enabled for events, false otherwise. + + + For more information see . + + + + + + + + Checks if this logger is enabled for the level. + + + true if this logger is enabled for events, false otherwise. + + + For more information see . + + + + + + + + Checks if this logger is enabled for the level. + + + true if this logger is enabled for events, false otherwise. + + + For more information see . + + + + + + + + Construct a new wrapper for the specified logger. + + The logger to wrap. + + + Construct a new wrapper for the specified logger. + + + + + + Virtual method called when the configuration of the repository changes + + the repository holding the levels + + + Virtual method called when the configuration of the repository changes + + + + + + Logs a message object with the DEBUG level. + + The message object to log. + + + This method first checks if this logger is DEBUG + enabled by comparing the level of this logger with the + DEBUG level. If this logger is + DEBUG enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of the + additivity flag. + + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + + + + Logs a message object with the DEBUG level + + The message object to log. + The exception to log, including its stack trace. + + + Logs a message object with the DEBUG level including + the stack trace of the passed + as a parameter. + + + See the form for more detailed information. + + + + + + + Logs a formatted message string with the DEBUG level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the DEBUG level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the DEBUG level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the DEBUG level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the DEBUG level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a message object with the INFO level. + + The message object to log. + + + This method first checks if this logger is INFO + enabled by comparing the level of this logger with the + INFO level. If this logger is + INFO enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger + and also higher in the hierarchy depending on the value of + the additivity flag. + + + WARNING Note that passing an + to this method will print the name of the + but no stack trace. To print a stack trace use the + form instead. + + + + + + Logs a message object with the INFO level. + + The message object to log. + The exception to log, including its stack trace. + + + Logs a message object with the INFO level including + the stack trace of the + passed as a parameter. + + + See the form for more detailed information. + + + + + + + Logs a formatted message string with the INFO level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the INFO level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the INFO level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the INFO level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the INFO level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a message object with the WARN level. + + the message object to log + + + This method first checks if this logger is WARN + enabled by comparing the level of this logger with the + WARN level. If this logger is + WARN enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger and + also higher in the hierarchy depending on the value of the + additivity flag. + + + WARNING Note that passing an to this + method will print the name of the but no + stack trace. To print a stack trace use the + form instead. + + + + + + Logs a message object with the WARN level + + The message object to log. + The exception to log, including its stack trace. + + + Logs a message object with the WARN level including + the stack trace of the + passed as a parameter. + + + See the form for more detailed information. + + + + + + + Logs a formatted message string with the WARN level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the WARN level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the WARN level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the WARN level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the WARN level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a message object with the ERROR level. + + The message object to log. + + + This method first checks if this logger is ERROR + enabled by comparing the level of this logger with the + ERROR level. If this logger is + ERROR enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger and + also higher in the hierarchy depending on the value of the + additivity flag. + + + WARNING Note that passing an to this + method will print the name of the but no + stack trace. To print a stack trace use the + form instead. + + + + + + Logs a message object with the ERROR level + + The message object to log. + The exception to log, including its stack trace. + + + Logs a message object with the ERROR level including + the stack trace of the + passed as a parameter. + + + See the form for more detailed information. + + + + + + + Logs a formatted message string with the ERROR level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the ERROR level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the ERROR level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the ERROR level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the ERROR level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a message object with the FATAL level. + + The message object to log. + + + This method first checks if this logger is FATAL + enabled by comparing the level of this logger with the + FATAL level. If this logger is + FATAL enabled, then it converts the message object + (passed as parameter) to a string by invoking the appropriate + . It then + proceeds to call all the registered appenders in this logger and + also higher in the hierarchy depending on the value of the + additivity flag. + + + WARNING Note that passing an to this + method will print the name of the but no + stack trace. To print a stack trace use the + form instead. + + + + + + Logs a message object with the FATAL level + + The message object to log. + The exception to log, including its stack trace. + + + Logs a message object with the FATAL level including + the stack trace of the + passed as a parameter. + + + See the form for more detailed information. + + + + + + + Logs a formatted message string with the FATAL level. + + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the FATAL level. + + A String containing zero or more format items + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the FATAL level. + + A String containing zero or more format items + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the FATAL level. + + A String containing zero or more format items + An Object to format + An Object to format + An Object to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + The string is formatted using the + format provider. To specify a localized provider use the + method. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Logs a formatted message string with the FATAL level. + + An that supplies culture-specific formatting information + A String containing zero or more format items + An Object array containing zero or more objects to format + + + The message is formatted using the method. See + String.Format for details of the syntax of the format string and the behavior + of the formatting. + + + This method does not take an object to include in the + log event. To pass an use one of the + methods instead. + + + + + + Event handler for the event + + the repository + Empty + + + + The fully qualified name of this declaring type not the type of any subclass. + + + + + Checks if this logger is enabled for the DEBUG + level. + + + true if this logger is enabled for DEBUG events, + false otherwise. + + + + This function is intended to lessen the computational cost of + disabled log debug statements. + + + For some log Logger object, when you write: + + + log.Debug("This is entry number: " + i ); + + + You incur the cost constructing the message, concatenation in + this case, regardless of whether the message is logged or not. + + + If you are worried about speed, then you should write: + + + if (log.IsDebugEnabled()) + { + log.Debug("This is entry number: " + i ); + } + + + This way you will not incur the cost of parameter + construction if debugging is disabled for log. On + the other hand, if the log is debug enabled, you + will incur the cost of evaluating whether the logger is debug + enabled twice. Once in IsDebugEnabled and once in + the Debug. This is an insignificant overhead + since evaluating a logger takes about 1% of the time it + takes to actually log. + + + + + + Checks if this logger is enabled for the INFO level. + + + true if this logger is enabled for INFO events, + false otherwise. + + + + See for more information and examples + of using this method. + + + + + + + Checks if this logger is enabled for the WARN level. + + + true if this logger is enabled for WARN events, + false otherwise. + + + + See for more information and examples + of using this method. + + + + + + + Checks if this logger is enabled for the ERROR level. + + + true if this logger is enabled for ERROR events, + false otherwise. + + + + See for more information and examples of using this method. + + + + + + + Checks if this logger is enabled for the FATAL level. + + + true if this logger is enabled for FATAL events, + false otherwise. + + + + See for more information and examples of using this method. + + + + + + + A SecurityContext used by log4net when interacting with protected resources + + + + A SecurityContext used by log4net when interacting with protected resources + for example with operating system services. This can be used to impersonate + a principal that has been granted privileges on the system resources. + + + Nicko Cadell + + + + Impersonate this SecurityContext + + State supplied by the caller + An instance that will + revoke the impersonation of this SecurityContext, or null + + + Impersonate this security context. Further calls on the current + thread should now be made in the security context provided + by this object. When the result + method is called the security + context of the thread should be reverted to the state it was in + before was called. + + + + + + The providers default instances. + + + + A configured component that interacts with potentially protected system + resources uses a to provide the elevated + privileges required. If the object has + been not been explicitly provided to the component then the component + will request one from this . + + + By default the is + an instance of which returns only + objects. This is a reasonable default + where the privileges required are not know by the system. + + + This default behavior can be overridden by subclassing the + and overriding the method to return + the desired objects. The default provider + can be replaced by programmatically setting the value of the + property. + + + An alternative is to use the log4net.Config.SecurityContextProviderAttribute + This attribute can be applied to an assembly in the same way as the + log4net.Config.XmlConfiguratorAttribute". The attribute takes + the type to use as the as an argument. + + + Nicko Cadell + + + + The default provider + + + + + Protected default constructor to allow subclassing + + + + Protected default constructor to allow subclassing + + + + + + Create a SecurityContext for a consumer + + The consumer requesting the SecurityContext + An impersonation context + + + The default implementation is to return a . + + + Subclasses should override this method to provide their own + behavior. + + + + + + Gets or sets the default SecurityContextProvider + + + The default SecurityContextProvider + + + + The default provider is used by configured components that + require a and have not had one + given to them. + + + By default this is an instance of + that returns objects. + + + The default provider can be set programmatically by setting + the value of this property to a sub class of + that has the desired behavior. + + + + + + Delegate used to handle creation of new wrappers. + + The logger to wrap in a wrapper. + + + Delegate used to handle creation of new wrappers. This delegate + is called from the + method to construct the wrapper for the specified logger. + + + The delegate to use is supplied to the + constructor. + + + + + + Maps between logger objects and wrapper objects. + + + + This class maintains a mapping between objects and + objects. Use the method to + lookup the for the specified . + + + New wrapper instances are created by the + method. The default behavior is for this method to delegate construction + of the wrapper to the delegate supplied + to the constructor. This allows specialization of the behavior without + requiring subclassing of this type. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the + + The handler to use to create the wrapper objects. + + + Initializes a new instance of the class with + the specified handler to create the wrapper objects. + + + + + + Gets the wrapper object for the specified logger. + + The wrapper object for the specified logger + + + If the logger is null then the corresponding wrapper is null. + + + Looks up the wrapper it it has previously been requested and + returns it. If the wrapper has never been requested before then + the virtual method is + called. + + + + + + Creates the wrapper object for the specified logger. + + The logger to wrap in a wrapper. + The wrapper object for the logger. + + + This implementation uses the + passed to the constructor to create the wrapper. This method + can be overridden in a subclass. + + + + + + Called when a monitored repository shutdown event is received. + + The that is shutting down + + + This method is called when a that this + is holding loggers for has signaled its shutdown + event . The default + behavior of this method is to release the references to the loggers + and their wrappers generated for this repository. + + + + + + Event handler for repository shutdown event. + + The sender of the event. + The event args. + + + + Map of logger repositories to hashtables of ILogger to ILoggerWrapper mappings + + + + + The handler to use to create the extension wrapper objects. + + + + + Internal reference to the delegate used to register for repository shutdown events. + + + + + Gets the map of logger repositories. + + + Map of logger repositories. + + + + Gets the hashtable that is keyed on . The + values are hashtables keyed on with the + value being the corresponding . + + + + + + Formats a as "HH:mm:ss,fff". + + + + Formats a in the format "HH:mm:ss,fff" for example, "15:49:37,459". + + + Nicko Cadell + Gert Driesen + + + + Render a as a string. + + + + Interface to abstract the rendering of a + instance into a string. + + + The method is used to render the + date to a text writer. + + + Nicko Cadell + Gert Driesen + + + + Formats the specified date as a string. + + The date to format. + The writer to write to. + + + Format the as a string and write it + to the provided. + + + + + + String constant used to specify AbsoluteTimeDateFormat in layouts. Current value is ABSOLUTE. + + + + + String constant used to specify DateTimeDateFormat in layouts. Current value is DATE. + + + + + String constant used to specify ISO8601DateFormat in layouts. Current value is ISO8601. + + + + + Renders the date into a string. Format is "HH:mm:ss". + + The date to render into a string. + The string builder to write to. + + + Subclasses should override this method to render the date + into a string using a precision up to the second. This method + will be called at most once per second and the result will be + reused if it is needed again during the same second. + + + + + + Renders the date into a string. Format is "HH:mm:ss,fff". + + The date to render into a string. + The writer to write to. + + + Uses the method to generate the + time string up to the seconds and then appends the current + milliseconds. The results from are + cached and is called at most once + per second. + + + Sub classes should override + rather than . + + + + + + Last stored time with precision up to the second. + + + + + Last stored time with precision up to the second, formatted + as a string. + + + + + Last stored time with precision up to the second, formatted + as a string. + + + + + Formats a as "dd MMM yyyy HH:mm:ss,fff" + + + + Formats a in the format + "dd MMM yyyy HH:mm:ss,fff" for example, + "06 Nov 1994 15:49:37,459". + + + Nicko Cadell + Gert Driesen + Angelika Schnagl + + + + Default constructor. + + + + Initializes a new instance of the class. + + + + + + Formats the date without the milliseconds part + + The date to format. + The string builder to write to. + + + Formats a DateTime in the format "dd MMM yyyy HH:mm:ss" + for example, "06 Nov 1994 15:49:37". + + + The base class will append the ",fff" milliseconds section. + This method will only be called at most once per second. + + + + + + The format info for the invariant culture. + + + + + Formats the as "yyyy-MM-dd HH:mm:ss,fff". + + + + Formats the specified as a string: "yyyy-MM-dd HH:mm:ss,fff". + + + Nicko Cadell + Gert Driesen + + + + Default constructor + + + + Initializes a new instance of the class. + + + + + + Formats the date without the milliseconds part + + The date to format. + The string builder to write to. + + + Formats the date specified as a string: "yyyy-MM-dd HH:mm:ss". + + + The base class will append the ",fff" milliseconds section. + This method will only be called at most once per second. + + + + + + Formats the using the method. + + + + Formats the using the method. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + The format string. + + + Initializes a new instance of the class + with the specified format string. + + + The format string must be compatible with the options + that can be supplied to . + + + + + + Formats the date using . + + The date to convert to a string. + The writer to write to. + + + Uses the date format string supplied to the constructor to call + the method to format the date. + + + + + + The format string used to format the . + + + + The format string must be compatible with the options + that can be supplied to . + + + + + + This filter drops all . + + + + You can add this filter to the end of a filter chain to + switch from the default "accept all unless instructed otherwise" + filtering behavior to a "deny all unless instructed otherwise" + behavior. + + + Nicko Cadell + Gert Driesen + + + + Subclass this type to implement customized logging event filtering + + + + Users should extend this class to implement customized logging + event filtering. Note that and + , the parent class of all standard + appenders, have built-in filtering rules. It is suggested that you + first use and understand the built-in rules before rushing to write + your own custom filters. + + + This abstract class assumes and also imposes that filters be + organized in a linear chain. The + method of each filter is called sequentially, in the order of their + addition to the chain. + + + The method must return one + of the integer constants , + or . + + + If the value is returned, then the log event is dropped + immediately without consulting with the remaining filters. + + + If the value is returned, then the next filter + in the chain is consulted. If there are no more filters in the + chain, then the log event is logged. Thus, in the presence of no + filters, the default behavior is to log all logging events. + + + If the value is returned, then the log + event is logged without consulting the remaining filters. + + + The philosophy of log4net filters is largely inspired from the + Linux ipchains. + + + Nicko Cadell + Gert Driesen + + + + Implement this interface to provide customized logging event filtering + + + + Users should implement this interface to implement customized logging + event filtering. Note that and + , the parent class of all standard + appenders, have built-in filtering rules. It is suggested that you + first use and understand the built-in rules before rushing to write + your own custom filters. + + + This abstract class assumes and also imposes that filters be + organized in a linear chain. The + method of each filter is called sequentially, in the order of their + addition to the chain. + + + The method must return one + of the integer constants , + or . + + + If the value is returned, then the log event is dropped + immediately without consulting with the remaining filters. + + + If the value is returned, then the next filter + in the chain is consulted. If there are no more filters in the + chain, then the log event is logged. Thus, in the presence of no + filters, the default behavior is to log all logging events. + + + If the value is returned, then the log + event is logged without consulting the remaining filters. + + + The philosophy of log4net filters is largely inspired from the + Linux ipchains. + + + Nicko Cadell + Gert Driesen + + + + Decide if the logging event should be logged through an appender. + + The LoggingEvent to decide upon + The decision of the filter + + + If the decision is , then the event will be + dropped. If the decision is , then the next + filter, if any, will be invoked. If the decision is then + the event will be logged without consulting with other filters in + the chain. + + + + + + Property to get and set the next filter + + + The next filter in the chain + + + + Filters are typically composed into chains. This property allows the next filter in + the chain to be accessed. + + + + + + Points to the next filter in the filter chain. + + + + See for more information. + + + + + + Initialize the filter with the options set + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + Typically filter's options become active immediately on set, + however this method must still be called. + + + + + + Decide if the should be logged through an appender. + + The to decide upon + The decision of the filter + + + If the decision is , then the event will be + dropped. If the decision is , then the next + filter, if any, will be invoked. If the decision is then + the event will be logged without consulting with other filters in + the chain. + + + This method is marked abstract and must be implemented + in a subclass. + + + + + + Property to get and set the next filter + + + The next filter in the chain + + + + Filters are typically composed into chains. This property allows the next filter in + the chain to be accessed. + + + + + + Default constructor + + + + + Always returns the integer constant + + the LoggingEvent to filter + Always returns + + + Ignores the event being logged and just returns + . This can be used to change the default filter + chain behavior from to . This filter + should only be used as the last filter in the chain + as any further filters will be ignored! + + + + + + The return result from + + + + The return result from + + + + + + The log event must be dropped immediately without + consulting with the remaining filters, if any, in the chain. + + + + + This filter is neutral with respect to the log event. + The remaining filters, if any, should be consulted for a final decision. + + + + + The log event must be logged immediately without + consulting with the remaining filters, if any, in the chain. + + + + + This is a very simple filter based on matching. + + + + The filter admits two options and + . If there is an exact match between the value + of the option and the of the + , then the method returns in + case the option value is set + to true, if it is false then + is returned. If the does not match then + the result will be . + + + Nicko Cadell + Gert Driesen + + + + flag to indicate if the filter should on a match + + + + + the to match against + + + + + Default constructor + + + + + Tests if the of the logging event matches that of the filter + + the event to filter + see remarks + + + If the of the event matches the level of the + filter then the result of the function depends on the + value of . If it is true then + the function will return , it it is false then it + will return . If the does not match then + the result will be . + + + + + + when matching + + + + The property is a flag that determines + the behavior when a matching is found. If the + flag is set to true then the filter will the + logging event, otherwise it will the event. + + + The default is true i.e. to the event. + + + + + + The that the filter will match + + + + The level that this filter will attempt to match against the + level. If a match is found then + the result depends on the value of . + + + + + + This is a simple filter based on matching. + + + + The filter admits three options and + that determine the range of priorities that are matched, and + . If there is a match between the range + of priorities and the of the , then the + method returns in case the + option value is set to true, if it is false + then is returned. If there is no match, is returned. + + + Nicko Cadell + Gert Driesen + + + + Flag to indicate the behavior when matching a + + + + + the minimum value to match + + + + + the maximum value to match + + + + + Default constructor + + + + + Check if the event should be logged. + + the logging event to check + see remarks + + + If the of the logging event is outside the range + matched by this filter then + is returned. If the is matched then the value of + is checked. If it is true then + is returned, otherwise + is returned. + + + + + + when matching and + + + + The property is a flag that determines + the behavior when a matching is found. If the + flag is set to true then the filter will the + logging event, otherwise it will the event. + + + The default is true i.e. to the event. + + + + + + Set the minimum matched + + + + The minimum level that this filter will attempt to match against the + level. If a match is found then + the result depends on the value of . + + + + + + Sets the maximum matched + + + + The maximum level that this filter will attempt to match against the + level. If a match is found then + the result depends on the value of . + + + + + + Simple filter to match a string in the event's logger name. + + + + The works very similar to the . It admits two + options and . If the + of the starts + with the value of the option, then the + method returns in + case the option value is set to true, + if it is false then is returned. + + + Daniel Cazzulino + + + + Flag to indicate the behavior when we have a match + + + + + The logger name string to substring match against the event + + + + + Default constructor + + + + + Check if this filter should allow the event to be logged + + the event being logged + see remarks + + + The rendered message is matched against the . + If the equals the beginning of + the incoming () + then a match will have occurred. If no match occurs + this function will return + allowing other filters to check the event. If a match occurs then + the value of is checked. If it is + true then is returned otherwise + is returned. + + + + + + when matching + + + + The property is a flag that determines + the behavior when a matching is found. If the + flag is set to true then the filter will the + logging event, otherwise it will the event. + + + The default is true i.e. to the event. + + + + + + The that the filter will match + + + + This filter will attempt to match this value against logger name in + the following way. The match will be done against the beginning of the + logger name (using ). The match is + case sensitive. If a match is found then + the result depends on the value of . + + + + + + Simple filter to match a keyed string in the + + + + Simple filter to match a keyed string in the + + + As the MDC has been replaced with layered properties the + should be used instead. + + + Nicko Cadell + Gert Driesen + + + + Simple filter to match a string an event property + + + + Simple filter to match a string in the value for a + specific event property + + + Nicko Cadell + + + + Simple filter to match a string in the rendered message + + + + Simple filter to match a string in the rendered message + + + Nicko Cadell + Gert Driesen + + + + Flag to indicate the behavior when we have a match + + + + + The string to substring match against the message + + + + + A string regex to match + + + + + A regex object to match (generated from m_stringRegexToMatch) + + + + + Default constructor + + + + + Initialize and precompile the Regex if required + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Check if this filter should allow the event to be logged + + the event being logged + see remarks + + + The rendered message is matched against the . + If the occurs as a substring within + the message then a match will have occurred. If no match occurs + this function will return + allowing other filters to check the event. If a match occurs then + the value of is checked. If it is + true then is returned otherwise + is returned. + + + + + + when matching or + + + + The property is a flag that determines + the behavior when a matching is found. If the + flag is set to true then the filter will the + logging event, otherwise it will the event. + + + The default is true i.e. to the event. + + + + + + Sets the static string to match + + + + The string that will be substring matched against + the rendered message. If the message contains this + string then the filter will match. If a match is found then + the result depends on the value of . + + + One of or + must be specified. + + + + + + Sets the regular expression to match + + + + The regular expression pattern that will be matched against + the rendered message. If the message matches this + pattern then the filter will match. If a match is found then + the result depends on the value of . + + + One of or + must be specified. + + + + + + The key to use to lookup the string from the event properties + + + + + Default constructor + + + + + Check if this filter should allow the event to be logged + + the event being logged + see remarks + + + The event property for the is matched against + the . + If the occurs as a substring within + the property value then a match will have occurred. If no match occurs + this function will return + allowing other filters to check the event. If a match occurs then + the value of is checked. If it is + true then is returned otherwise + is returned. + + + + + + The key to lookup in the event properties and then match against. + + + + The key name to use to lookup in the properties map of the + . The match will be performed against + the value of this property if it exists. + + + + + + Simple filter to match a string in the + + + + Simple filter to match a string in the + + + As the MDC has been replaced with named stacks stored in the + properties collections the should + be used instead. + + + Nicko Cadell + Gert Driesen + + + + Default constructor + + + + Sets the to "NDC". + + + + + + Write the event appdomain name to the output + + + + Writes the to the output writer. + + + Daniel Cazzulino + Nicko Cadell + + + + Abstract class that provides the formatting functionality that + derived classes need. + + + Conversion specifiers in a conversion patterns are parsed to + individual PatternConverters. Each of which is responsible for + converting a logging event in a converter specific manner. + + Nicko Cadell + + + + Abstract class that provides the formatting functionality that + derived classes need. + + + + Conversion specifiers in a conversion patterns are parsed to + individual PatternConverters. Each of which is responsible for + converting a logging event in a converter specific manner. + + + Nicko Cadell + Gert Driesen + + + + Initial buffer size + + + + + Maximum buffer size before it is recycled + + + + + Protected constructor + + + + Initializes a new instance of the class. + + + + + + Evaluate this pattern converter and write the output to a writer. + + that will receive the formatted result. + The state object on which the pattern converter should be executed. + + + Derived pattern converters must override this method in order to + convert conversion specifiers in the appropriate way. + + + + + + Set the next pattern converter in the chains + + the pattern converter that should follow this converter in the chain + the next converter + + + The PatternConverter can merge with its neighbor during this method (or a sub class). + Therefore the return value may or may not be the value of the argument passed in. + + + + + + Write the pattern converter to the writer with appropriate formatting + + that will receive the formatted result. + The state object on which the pattern converter should be executed. + + + This method calls to allow the subclass to perform + appropriate conversion of the pattern converter. If formatting options have + been specified via the then this method will + apply those formattings before writing the output. + + + + + + Fast space padding method. + + to which the spaces will be appended. + The number of spaces to be padded. + + + Fast space padding method. + + + + + + The option string to the converter + + + + + Write an dictionary to a + + the writer to write to + a to use for object conversion + the value to write to the writer + + + Writes the to a writer in the form: + + + {key1=value1, key2=value2, key3=value3} + + + If the specified + is not null then it is used to render the key and value to text, otherwise + the object's ToString method is called. + + + + + + Write an object to a + + the writer to write to + a to use for object conversion + the value to write to the writer + + + Writes the Object to a writer. If the specified + is not null then it is used to render the object to text, otherwise + the object's ToString method is called. + + + + + + Get the next pattern converter in the chain + + + the next pattern converter in the chain + + + + Get the next pattern converter in the chain + + + + + + Gets or sets the formatting info for this converter + + + The formatting info for this converter + + + + Gets or sets the formatting info for this converter + + + + + + Gets or sets the option value for this converter + + + The option for this converter + + + + Gets or sets the option value for this converter + + + + + + Initializes a new instance of the class. + + + + + Derived pattern converters must override this method in order to + convert conversion specifiers in the correct way. + + that will receive the formatted result. + The on which the pattern converter should be executed. + + + + Derived pattern converters must override this method in order to + convert conversion specifiers in the correct way. + + that will receive the formatted result. + The state object on which the pattern converter should be executed. + + + + Flag indicating if this converter handles exceptions + + + false if this converter handles exceptions + + + + + Flag indicating if this converter handles the logging event exception + + false if this converter handles the logging event exception + + + If this converter handles the exception object contained within + , then this property should be set to + false. Otherwise, if the layout ignores the exception + object, then the property should be set to true. + + + Set this value to override a this default setting. The default + value is true, this converter does not handle the exception. + + + + + + Write the event appdomain name to the output + + that will receive the formatted result. + the event being logged + + + Writes the to the output . + + + + + + Date pattern converter, uses a to format + the date of a . + + + + Render the to the writer as a string. + + + The value of the determines + the formatting of the date. The following values are allowed: + + + Option value + Output + + + ISO8601 + + Uses the formatter. + Formats using the "yyyy-MM-dd HH:mm:ss,fff" pattern. + + + + DATE + + Uses the formatter. + Formats using the "dd MMM yyyy HH:mm:ss,fff" for example, "06 Nov 1994 15:49:37,459". + + + + ABSOLUTE + + Uses the formatter. + Formats using the "HH:mm:ss,yyyy" for example, "15:49:37,459". + + + + other + + Any other pattern string uses the formatter. + This formatter passes the pattern string to the + method. + For details on valid patterns see + DateTimeFormatInfo Class. + + + + + + The is in the local time zone and is rendered in that zone. + To output the time in Universal time see . + + + Nicko Cadell + + + + The used to render the date to a string + + + + The used to render the date to a string + + + + + + Initialize the converter pattern based on the property. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Convert the pattern into the rendered message + + that will receive the formatted result. + the event being logged + + + Pass the to the + for it to render it to the writer. + + + The passed is in the local time zone. + + + + + + Write the exception text to the output + + + + If an exception object is stored in the logging event + it will be rendered into the pattern output with a + trailing newline. + + + If there is no exception then nothing will be output + and no trailing newline will be appended. + It is typical to put a newline before the exception + and to have the exception as the last data in the pattern. + + + Nicko Cadell + + + + Default constructor + + + + + Write the exception text to the output + + that will receive the formatted result. + the event being logged + + + If an exception object is stored in the logging event + it will be rendered into the pattern output with a + trailing newline. + + + If there is no exception then nothing will be output + and no trailing newline will be appended. + It is typical to put a newline before the exception + and to have the exception as the last data in the pattern. + + + + + + Writes the caller location file name to the output + + + + Writes the value of the for + the event to the output writer. + + + Nicko Cadell + + + + Write the caller location file name to the output + + that will receive the formatted result. + the event being logged + + + Writes the value of the for + the to the output . + + + + + + Write the caller location info to the output + + + + Writes the to the output writer. + + + Nicko Cadell + + + + Write the caller location info to the output + + that will receive the formatted result. + the event being logged + + + Writes the to the output writer. + + + + + + Writes the event identity to the output + + + + Writes the value of the to + the output writer. + + + Daniel Cazzulino + Nicko Cadell + + + + Writes the event identity to the output + + that will receive the formatted result. + the event being logged + + + Writes the value of the + to + the output . + + + + + + Write the event level to the output + + + + Writes the display name of the event + to the writer. + + + Nicko Cadell + + + + Write the event level to the output + + that will receive the formatted result. + the event being logged + + + Writes the of the + to the . + + + + + + Write the caller location line number to the output + + + + Writes the value of the for + the event to the output writer. + + + Nicko Cadell + + + + Write the caller location line number to the output + + that will receive the formatted result. + the event being logged + + + Writes the value of the for + the to the output . + + + + + + Converter for logger name + + + + Outputs the of the event. + + + Nicko Cadell + + + + Converter to output and truncate '.' separated strings + + + + This abstract class supports truncating a '.' separated string + to show a specified number of elements from the right hand side. + This is used to truncate class names that are fully qualified. + + + Subclasses should override the method to + return the fully qualified string. + + + Nicko Cadell + + + + Initialize the converter + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Get the fully qualified string data + + the event being logged + the fully qualified name + + + Overridden by subclasses to get the fully qualified name before the + precision is applied to it. + + + Return the fully qualified '.' (dot/period) separated string. + + + + + + Convert the pattern to the rendered message + + that will receive the formatted result. + the event being logged + + Render the to the precision + specified by the property. + + + + + Gets the fully qualified name of the logger + + the event being logged + The fully qualified logger name + + + Returns the of the . + + + + + + Writes the event message to the output + + + + Uses the method + to write out the event message. + + + Nicko Cadell + + + + Writes the event message to the output + + that will receive the formatted result. + the event being logged + + + Uses the method + to write out the event message. + + + + + + Write the method name to the output + + + + Writes the caller location to + the output. + + + Nicko Cadell + + + + Write the method name to the output + + that will receive the formatted result. + the event being logged + + + Writes the caller location to + the output. + + + + + + Converter to include event NDC + + + + Outputs the value of the event property named NDC. + + + The should be used instead. + + + Nicko Cadell + + + + Write the event NDC to the output + + that will receive the formatted result. + the event being logged + + + As the thread context stacks are now stored in named event properties + this converter simply looks up the value of the NDC property. + + + The should be used instead. + + + + + + Property pattern converter + + + + Writes out the value of a named property. The property name + should be set in the + property. + + + If the is set to null + then all the properties are written as key value pairs. + + + Nicko Cadell + + + + Write the property value to the output + + that will receive the formatted result. + the event being logged + + + Writes out the value of a named property. The property name + should be set in the + property. + + + If the is set to null + then all the properties are written as key value pairs. + + + + + + Converter to output the relative time of the event + + + + Converter to output the time of the event relative to the start of the program. + + + Nicko Cadell + + + + Write the relative time to the output + + that will receive the formatted result. + the event being logged + + + Writes out the relative time of the event in milliseconds. + That is the number of milliseconds between the event + and the . + + + + + + Helper method to get the time difference between two DateTime objects + + start time (in the current local time zone) + end time (in the current local time zone) + the time difference in milliseconds + + + + Converter to include event thread name + + + + Writes the to the output. + + + Nicko Cadell + + + + Write the ThreadName to the output + + that will receive the formatted result. + the event being logged + + + Writes the to the . + + + + + + Pattern converter for the class name + + + + Outputs the of the event. + + + Nicko Cadell + + + + Gets the fully qualified name of the class + + the event being logged + The fully qualified type name for the caller location + + + Returns the of the . + + + + + + Converter to include event user name + + Douglas de la Torre + Nicko Cadell + + + + Convert the pattern to the rendered message + + that will receive the formatted result. + the event being logged + + + + Write the TimeStamp to the output + + + + Date pattern converter, uses a to format + the date of a . + + + Uses a to format the + in Universal time. + + + See the for details on the date pattern syntax. + + + + Nicko Cadell + + + + Write the TimeStamp to the output + + that will receive the formatted result. + the event being logged + + + Pass the to the + for it to render it to the writer. + + + The passed is in the local time zone, this is converted + to Universal time before it is rendered. + + + + + + + A Layout that renders only the Exception text from the logging event + + + + A Layout that renders only the Exception text from the logging event. + + + This Layout should only be used with appenders that utilize multiple + layouts (e.g. ). + + + Nicko Cadell + Gert Driesen + + + + Extend this abstract class to create your own log layout format. + + + + This is the base implementation of the + interface. Most layout objects should extend this class. + + + + + + Subclasses must implement the + method. + + + Subclasses should set the in their default + constructor. + + + + Nicko Cadell + Gert Driesen + + + + Interface implemented by layout objects + + + + An object is used to format a + as text. The method is called by an + appender to transform the into a string. + + + The layout can also supply and + text that is appender before any events and after all the events respectively. + + + Nicko Cadell + Gert Driesen + + + + Implement this method to create your own layout format. + + The TextWriter to write the formatted event to + The event to format + + + This method is called by an appender to format + the as text and output to a writer. + + + If the caller does not have a and prefers the + event to be formatted as a then the following + code can be used to format the event into a . + + + StringWriter writer = new StringWriter(); + Layout.Format(writer, loggingEvent); + string formattedEvent = writer.ToString(); + + + + + + The content type output by this layout. + + The content type + + + The content type output by this layout. + + + This is a MIME type e.g. "text/plain". + + + + + + The header for the layout format. + + the layout header + + + The Header text will be appended before any logging events + are formatted and appended. + + + + + + The footer for the layout format. + + the layout footer + + + The Footer text will be appended after all the logging events + have been formatted and appended. + + + + + + Flag indicating if this layout handle exceptions + + false if this layout handles exceptions + + + If this layout handles the exception object contained within + , then the layout should return + false. Otherwise, if the layout ignores the exception + object, then the layout should return true. + + + + + + The header text + + + + See for more information. + + + + + + The footer text + + + + See for more information. + + + + + + Flag indicating if this layout handles exceptions + + + + false if this layout handles exceptions + + + + + + Empty default constructor + + + + Empty default constructor + + + + + + Activate component options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + This method must be implemented by the subclass. + + + + + + Implement this method to create your own layout format. + + The TextWriter to write the formatted event to + The event to format + + + This method is called by an appender to format + the as text. + + + + + + The content type output by this layout. + + The content type is "text/plain" + + + The content type output by this layout. + + + This base class uses the value "text/plain". + To change this value a subclass must override this + property. + + + + + + The header for the layout format. + + the layout header + + + The Header text will be appended before any logging events + are formatted and appended. + + + + + + The footer for the layout format. + + the layout footer + + + The Footer text will be appended after all the logging events + have been formatted and appended. + + + + + + Flag indicating if this layout handles exceptions + + false if this layout handles exceptions + + + If this layout handles the exception object contained within + , then the layout should return + false. Otherwise, if the layout ignores the exception + object, then the layout should return true. + + + Set this value to override a this default setting. The default + value is true, this layout does not handle the exception. + + + + + + Default constructor + + + + Constructs a ExceptionLayout + + + + + + Activate component options + + + + Part of the component activation + framework. + + + This method does nothing as options become effective immediately. + + + + + + Gets the exception text from the logging event + + The TextWriter to write the formatted event to + the event being logged + + + Write the exception string to the . + The exception string is retrieved from . + + + + + + Interface for raw layout objects + + + + Interface used to format a + to an object. + + + This interface should not be confused with the + interface. This interface is used in + only certain specialized situations where a raw object is + required rather than a formatted string. The + is not generally useful than this interface. + + + Nicko Cadell + Gert Driesen + + + + Implement this method to create your own layout format. + + The event to format + returns the formatted event + + + Implement this method to create your own layout format. + + + + + + Adapts any to a + + + + Where an is required this adapter + allows a to be specified. + + + Nicko Cadell + Gert Driesen + + + + The layout to adapt + + + + + Construct a new adapter + + the layout to adapt + + + Create the adapter for the specified . + + + + + + Format the logging event as an object. + + The event to format + returns the formatted event + + + Format the logging event as an object. + + + Uses the object supplied to + the constructor to perform the formatting. + + + + + + A flexible layout configurable with pattern string. + + + + The goal of this class is to a + as a string. The results + depend on the conversion pattern. + + + The conversion pattern is closely related to the conversion + pattern of the printf function in C. A conversion pattern is + composed of literal text and format control expressions called + conversion specifiers. + + + You are free to insert any literal text within the conversion + pattern. + + + Each conversion specifier starts with a percent sign (%) and is + followed by optional format modifiers and a conversion + pattern name. The conversion pattern name specifies the type of + data, e.g. logger, level, date, thread name. The format + modifiers control such things as field width, padding, left and + right justification. The following is a simple example. + + + Let the conversion pattern be "%-5level [%thread]: %message%newline" and assume + that the log4net environment was set to use a PatternLayout. Then the + statements + + + ILog log = LogManager.GetLogger(typeof(TestApp)); + log.Debug("Message 1"); + log.Warn("Message 2"); + + would yield the output + + DEBUG [main]: Message 1 + WARN [main]: Message 2 + + + Note that there is no explicit separator between text and + conversion specifiers. The pattern parser knows when it has reached + the end of a conversion specifier when it reads a conversion + character. In the example above the conversion specifier + %-5level means the level of the logging event should be left + justified to a width of five characters. + + + The recognized conversion pattern names are: + + + + Conversion Pattern Name + Effect + + + a + Equivalent to appdomain + + + appdomain + + Used to output the friendly name of the AppDomain where the + logging event was generated. + + + + c + Equivalent to logger + + + C + Equivalent to type + + + class + Equivalent to type + + + d + Equivalent to date + + + date + + + Used to output the date of the logging event in the local time zone. + To output the date in universal time use the %utcdate pattern. + The date conversion + specifier may be followed by a date format specifier enclosed + between braces. For example, %date{HH:mm:ss,fff} or + %date{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is + given then ISO8601 format is + assumed (). + + + The date format specifier admits the same syntax as the + time pattern string of the . + + + For better results it is recommended to use the log4net date + formatters. These can be specified using one of the strings + "ABSOLUTE", "DATE" and "ISO8601" for specifying + , + and respectively + . For example, + %date{ISO8601} or %date{ABSOLUTE}. + + + These dedicated date formatters perform significantly + better than . + + + + + exception + + + Used to output the exception passed in with the log message. + + + If an exception object is stored in the logging event + it will be rendered into the pattern output with a + trailing newline. + If there is no exception then nothing will be output + and no trailing newline will be appended. + It is typical to put a newline before the exception + and to have the exception as the last data in the pattern. + + + + + F + Equivalent to file + + + file + + + Used to output the file name where the logging request was + issued. + + + WARNING Generating caller location information is + extremely slow. Its use should be avoided unless execution speed + is not an issue. + + + See the note below on the availability of caller location information. + + + + + identity + + + Used to output the user name for the currently active user + (Principal.Identity.Name). + + + WARNING Generating caller information is + extremely slow. Its use should be avoided unless execution speed + is not an issue. + + + + + l + Equivalent to location + + + L + Equivalent to line + + + location + + + Used to output location information of the caller which generated + the logging event. + + + The location information depends on the CLI implementation but + usually consists of the fully qualified name of the calling + method followed by the callers source the file name and line + number between parentheses. + + + The location information can be very useful. However, its + generation is extremely slow. Its use should be avoided + unless execution speed is not an issue. + + + See the note below on the availability of caller location information. + + + + + level + + + Used to output the level of the logging event. + + + + + line + + + Used to output the line number from where the logging request + was issued. + + + WARNING Generating caller location information is + extremely slow. Its use should be avoided unless execution speed + is not an issue. + + + See the note below on the availability of caller location information. + + + + + logger + + + Used to output the logger of the logging event. The + logger conversion specifier can be optionally followed by + precision specifier, that is a decimal constant in + brackets. + + + If a precision specifier is given, then only the corresponding + number of right most components of the logger name will be + printed. By default the logger name is printed in full. + + + For example, for the logger name "a.b.c" the pattern + %logger{2} will output "b.c". + + + + + m + Equivalent to message + + + M + Equivalent to method + + + message + + + Used to output the application supplied message associated with + the logging event. + + + + + mdc + + + The MDC (old name for the ThreadContext.Properties) is now part of the + combined event properties. This pattern is supported for compatibility + but is equivalent to property. + + + + + method + + + Used to output the method name where the logging request was + issued. + + + WARNING Generating caller location information is + extremely slow. Its use should be avoided unless execution speed + is not an issue. + + + See the note below on the availability of caller location information. + + + + + n + Equivalent to newline + + + newline + + + Outputs the platform dependent line separator character or + characters. + + + This conversion pattern offers the same performance as using + non-portable line separator strings such as "\n", or "\r\n". + Thus, it is the preferred way of specifying a line separator. + + + + + ndc + + + Used to output the NDC (nested diagnostic context) associated + with the thread that generated the logging event. + + + + + p + Equivalent to level + + + P + Equivalent to property + + + properties + Equivalent to property + + + property + + + Used to output the an event specific property. The key to + lookup must be specified within braces and directly following the + pattern specifier, e.g. %property{user} would include the value + from the property that is keyed by the string 'user'. Each property value + that is to be included in the log must be specified separately. + Properties are added to events by loggers or appenders. By default + the log4net:HostName property is set to the name of machine on + which the event was originally logged. + + + If no key is specified, e.g. %property then all the keys and their + values are printed in a comma separated list. + + + The properties of an event are combined from a number of different + contexts. These are listed below in the order in which they are searched. + + + + the event properties + + The event has that can be set. These + properties are specific to this event only. + + + + the thread properties + + The that are set on the current + thread. These properties are shared by all events logged on this thread. + + + + the global properties + + The that are set globally. These + properties are shared by all the threads in the AppDomain. + + + + + + + + r + Equivalent to timestamp + + + t + Equivalent to thread + + + timestamp + + + Used to output the number of milliseconds elapsed since the start + of the application until the creation of the logging event. + + + + + thread + + + Used to output the name of the thread that generated the + logging event. Uses the thread number if no name is available. + + + + + type + + + Used to output the fully qualified type name of the caller + issuing the logging request. This conversion specifier + can be optionally followed by precision specifier, that + is a decimal constant in brackets. + + + If a precision specifier is given, then only the corresponding + number of right most components of the class name will be + printed. By default the class name is output in fully qualified form. + + + For example, for the class name "log4net.Layout.PatternLayout", the + pattern %type{1} will output "PatternLayout". + + + WARNING Generating the caller class information is + slow. Thus, its use should be avoided unless execution speed is + not an issue. + + + See the note below on the availability of caller location information. + + + + + u + Equivalent to identity + + + username + + + Used to output the WindowsIdentity for the currently + active user. + + + WARNING Generating caller WindowsIdentity information is + extremely slow. Its use should be avoided unless execution speed + is not an issue. + + + + + utcdate + + + Used to output the date of the logging event in universal time. + The date conversion + specifier may be followed by a date format specifier enclosed + between braces. For example, %utcdate{HH:mm:ss,fff} or + %utcdate{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is + given then ISO8601 format is + assumed (). + + + The date format specifier admits the same syntax as the + time pattern string of the . + + + For better results it is recommended to use the log4net date + formatters. These can be specified using one of the strings + "ABSOLUTE", "DATE" and "ISO8601" for specifying + , + and respectively + . For example, + %utcdate{ISO8601} or %utcdate{ABSOLUTE}. + + + These dedicated date formatters perform significantly + better than . + + + + + w + Equivalent to username + + + x + Equivalent to ndc + + + X + Equivalent to mdc + + + % + + + The sequence %% outputs a single percent sign. + + + + + + The single letter patterns are deprecated in favor of the + longer more descriptive pattern names. + + + By default the relevant information is output as is. However, + with the aid of format modifiers it is possible to change the + minimum field width, the maximum field width and justification. + + + The optional format modifier is placed between the percent sign + and the conversion pattern name. + + + The first optional format modifier is the left justification + flag which is just the minus (-) character. Then comes the + optional minimum field width modifier. This is a decimal + constant that represents the minimum number of characters to + output. If the data item requires fewer characters, it is padded on + either the left or the right until the minimum width is + reached. The default is to pad on the left (right justify) but you + can specify right padding with the left justification flag. The + padding character is space. If the data item is larger than the + minimum field width, the field is expanded to accommodate the + data. The value is never truncated. + + + This behavior can be changed using the maximum field + width modifier which is designated by a period followed by a + decimal constant. If the data item is longer than the maximum + field, then the extra characters are removed from the + beginning of the data item and not from the end. For + example, it the maximum field width is eight and the data item is + ten characters long, then the first two characters of the data item + are dropped. This behavior deviates from the printf function in C + where truncation is done from the end. + + + Below are various format modifier examples for the logger + conversion specifier. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format modifierleft justifyminimum widthmaximum widthcomment
%20loggerfalse20none + + Left pad with spaces if the logger name is less than 20 + characters long. + +
%-20loggertrue20none + + Right pad with spaces if the logger + name is less than 20 characters long. + +
%.30loggerNAnone30 + + Truncate from the beginning if the logger + name is longer than 30 characters. + +
%20.30loggerfalse2030 + + Left pad with spaces if the logger name is shorter than 20 + characters. However, if logger name is longer than 30 characters, + then truncate from the beginning. + +
%-20.30loggertrue2030 + + Right pad with spaces if the logger name is shorter than 20 + characters. However, if logger name is longer than 30 characters, + then truncate from the beginning. + +
+
+ + Note about caller location information.
+ The following patterns %type %file %line %method %location %class %C %F %L %l %M + all generate caller location information. + Location information uses the System.Diagnostics.StackTrace class to generate + a call stack. The caller's information is then extracted from this stack. +
+ + + The System.Diagnostics.StackTrace class is not supported on the + .NET Compact Framework 1.0 therefore caller location information is not + available on that framework. + + + + + The System.Diagnostics.StackTrace class has this to say about Release builds: + + + "StackTrace information will be most informative with Debug build configurations. + By default, Debug builds include debug symbols, while Release builds do not. The + debug symbols contain most of the file, method name, line number, and column + information used in constructing StackFrame and StackTrace objects. StackTrace + might not report as many method calls as expected, due to code transformations + that occur during optimization." + + + This means that in a Release build the caller information may be incomplete or may + not exist at all! Therefore caller location information cannot be relied upon in a Release build. + + + + Additional pattern converters may be registered with a specific + instance using the method. + +
+ + This is a more detailed pattern. + %timestamp [%thread] %level %logger %ndc - %message%newline + + + A similar pattern except that the relative time is + right padded if less than 6 digits, thread name is right padded if + less than 15 characters and truncated if longer and the logger + name is left padded if shorter than 30 characters and truncated if + longer. + %-6timestamp [%15.15thread] %-5level %30.30logger %ndc - %message%newline + + Nicko Cadell + Gert Driesen + Douglas de la Torre + Daniel Cazzulino +
+ + + Default pattern string for log output. + + + + Default pattern string for log output. + Currently set to the string "%message%newline" + which just prints the application supplied message. + + + + + + A detailed conversion pattern + + + + A conversion pattern which includes Time, Thread, Logger, and Nested Context. + Current value is %timestamp [%thread] %level %logger %ndc - %message%newline. + + + + + + Internal map of converter identifiers to converter types. + + + + This static map is overridden by the m_converterRegistry instance map + + + + + + the pattern + + + + + the head of the pattern converter chain + + + + + patterns defined on this PatternLayout only + + + + + Initialize the global registry + + + + Defines the builtin global rules. + + + + + + Constructs a PatternLayout using the DefaultConversionPattern + + + + The default pattern just produces the application supplied message. + + + Note to Inheritors: This constructor calls the virtual method + . If you override this method be + aware that it will be called before your is called constructor. + + + As per the contract the + method must be called after the properties on this object have been + configured. + + + + + + Constructs a PatternLayout using the supplied conversion pattern + + the pattern to use + + + Note to Inheritors: This constructor calls the virtual method + . If you override this method be + aware that it will be called before your is called constructor. + + + When using this constructor the method + need not be called. This may not be the case when using a subclass. + + + + + + Create the pattern parser instance + + the pattern to parse + The that will format the event + + + Creates the used to parse the conversion string. Sets the + global and instance rules on the . + + + + + + Initialize layout options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Produces a formatted string as specified by the conversion pattern. + + the event being logged + The TextWriter to write the formatted event to + + + Parse the using the patter format + specified in the property. + + + + + + Add a converter to this PatternLayout + + the converter info + + + This version of the method is used by the configurator. + Programmatic users should use the alternative method. + + + + + + Add a converter to this PatternLayout + + the name of the conversion pattern for this converter + the type of the converter + + + Add a named pattern converter to this instance. This + converter will be used in the formatting of the event. + This method must be called before . + + + The specified must extend the + type. + + + + + + The pattern formatting string + + + + The ConversionPattern option. This is the string which + controls formatting and consists of a mix of literal content and + conversion specifiers. + + + + + + Wrapper class used to map converter names to converter types + + + + Pattern converter info class used during configuration to + pass to the + method. + + + + + + default constructor + + + + + Gets or sets the name of the conversion pattern + + + + The name of the pattern in the format string + + + + + + Gets or sets the type of the converter + + + + The value specified must extend the + type. + + + + + + Type converter for the interface + + + + Used to convert objects to the interface. + Supports converting from the interface to + the interface using the . + + + Nicko Cadell + Gert Driesen + + + + Interface supported by type converters + + + + This interface supports conversion from arbitrary types + to a single target type. See . + + + Nicko Cadell + Gert Driesen + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Test if the can be converted to the + type supported by this converter. + + + + + + Convert the source object to the type supported by this object + + the object to convert + the converted object + + + Converts the to the type supported + by this converter. + + + + + + Can the sourceType be converted to an + + the source to be to be converted + true if the source type can be converted to + + + Test if the can be converted to a + . Only is supported + as the . + + + + + + Convert the value to a object + + the value to convert + the object + + + Convert the object to a + object. If the object + is a then the + is used to adapt between the two interfaces, otherwise an + exception is thrown. + + + + + + Extract the value of a property from the + + + + Extract the value of a property from the + + + Nicko Cadell + + + + Constructs a RawPropertyLayout + + + + + Lookup the property for + + The event to format + returns property value + + + Looks up and returns the object value of the property + named . If there is no property defined + with than name then null will be returned. + + + + + + The name of the value to lookup in the LoggingEvent Properties collection. + + + Value to lookup in the LoggingEvent Properties collection + + + + String name of the property to lookup in the . + + + + + + Extract the date from the + + + + Extract the date from the + + + Nicko Cadell + Gert Driesen + + + + Constructs a RawTimeStampLayout + + + + + Gets the as a . + + The event to format + returns the time stamp + + + Gets the as a . + + + The time stamp is in local time. To format the time stamp + in universal time use . + + + + + + Extract the date from the + + + + Extract the date from the + + + Nicko Cadell + Gert Driesen + + + + Constructs a RawUtcTimeStampLayout + + + + + Gets the as a . + + The event to format + returns the time stamp + + + Gets the as a . + + + The time stamp is in universal time. To format the time stamp + in local time use . + + + + + + A very simple layout + + + + SimpleLayout consists of the level of the log statement, + followed by " - " and then the log message itself. For example, + + DEBUG - Hello world + + + + Nicko Cadell + Gert Driesen + + + + Constructs a SimpleLayout + + + + + Initialize layout options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Produces a simple formatted output. + + the event being logged + The TextWriter to write the formatted event to + + + Formats the event as the level of the even, + followed by " - " and then the log message itself. The + output is terminated by a newline. + + + + + + Layout that formats the log events as XML elements. + + + + The output of the consists of a series of + log4net:event elements. It does not output a complete well-formed XML + file. The output is designed to be included as an external entity + in a separate file to form a correct XML file. + + + For example, if abc is the name of the file where + the output goes, then a well-formed XML file would + be: + + + <?xml version="1.0" ?> + + <!DOCTYPE log4net:events SYSTEM "log4net-events.dtd" [<!ENTITY data SYSTEM "abc">]> + + <log4net:events version="1.2" xmlns:log4net="http://logging.apache.org/log4net/schemas/log4net-events-1.2> + &data; + </log4net:events> + + + This approach enforces the independence of the + and the appender where it is embedded. + + + The version attribute helps components to correctly + interpret output generated by . The value of + this attribute should be "1.2" for release 1.2 and later. + + + Alternatively the Header and Footer properties can be + configured to output the correct XML header, open tag and close tag. + When setting the Header and Footer properties it is essential + that the underlying data store not be appendable otherwise the data + will become invalid XML. + + + Nicko Cadell + Gert Driesen + + + + Layout that formats the log events as XML elements. + + + + This is an abstract class that must be subclassed by an implementation + to conform to a specific schema. + + + Deriving classes must implement the method. + + + Nicko Cadell + Gert Driesen + + + + Protected constructor to support subclasses + + + + Initializes a new instance of the class + with no location info. + + + + + + Protected constructor to support subclasses + + + + The parameter determines whether + location information will be output by the layout. If + is set to true, then the + file name and line number of the statement at the origin of the log + statement will be output. + + + If you are embedding this layout within an SMTPAppender + then make sure to set the LocationInfo option of that + appender as well. + + + + + + Initialize layout options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Produces a formatted string. + + The event being logged. + The TextWriter to write the formatted event to + + + Format the and write it to the . + + + This method creates an that writes to the + . The is passed + to the method. Subclasses should override the + method rather than this method. + + + + + + Does the actual writing of the XML. + + The writer to use to output the event to. + The event to write. + + + Subclasses should override this method to format + the as XML. + + + + + + Flag to indicate if location information should be included in + the XML events. + + + + + Writer adapter that ignores Close + + + + + The string to replace invalid chars with + + + + + Gets a value indicating whether to include location information in + the XML events. + + + true if location information should be included in the XML + events; otherwise, false. + + + + If is set to true, then the file + name and line number of the statement at the origin of the log + statement will be output. + + + If you are embedding this layout within an SMTPAppender + then make sure to set the LocationInfo option of that + appender as well. + + + + + + The string to replace characters that can not be expressed in XML with. + + + Not all characters may be expressed in XML. This property contains the + string to replace those that can not with. This defaults to a ?. Set it + to the empty string to simply remove offending characters. For more + details on the allowed character ranges see http://www.w3.org/TR/REC-xml/#charsets + Character replacement will occur in the log message, the property names + and the property values. + + + + + + + Gets the content type output by this layout. + + + As this is the XML layout, the value is always "text/xml". + + + + As this is the XML layout, the value is always "text/xml". + + + + + + Constructs an XmlLayout + + + + + Constructs an XmlLayout. + + + + The LocationInfo option takes a boolean value. By + default, it is set to false which means there will be no location + information output by this layout. If the the option is set to + true, then the file name and line number of the statement + at the origin of the log statement will be output. + + + If you are embedding this layout within an SmtpAppender + then make sure to set the LocationInfo option of that + appender as well. + + + + + + Initialize layout options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + Builds a cache of the element names + + + + + + Does the actual writing of the XML. + + The writer to use to output the event to. + The event to write. + + + Override the base class method + to write the to the . + + + + + + The prefix to use for all generated element names + + + + + The prefix to use for all element names + + + + The default prefix is log4net. Set this property + to change the prefix. If the prefix is set to an empty string + then no prefix will be written. + + + + + + Set whether or not to base64 encode the message. + + + + By default the log message will be written as text to the xml + output. This can cause problems when the message contains binary + data. By setting this to true the contents of the message will be + base64 encoded. If this is set then invalid character replacement + (see ) will not be performed + on the log message. + + + + + + Set whether or not to base64 encode the property values. + + + + By default the properties will be written as text to the xml + output. This can cause problems when one or more properties contain + binary data. By setting this to true the values of the properties + will be base64 encoded. If this is set then invalid character replacement + (see ) will not be performed + on the property values. + + + + + + Layout that formats the log events as XML elements compatible with the log4j schema + + + + Formats the log events according to the http://logging.apache.org/log4j schema. + + + Nicko Cadell + + + + The 1st of January 1970 in UTC + + + + + Constructs an XMLLayoutSchemaLog4j + + + + + Constructs an XMLLayoutSchemaLog4j. + + + + The LocationInfo option takes a boolean value. By + default, it is set to false which means there will be no location + information output by this layout. If the the option is set to + true, then the file name and line number of the statement + at the origin of the log statement will be output. + + + If you are embedding this layout within an SMTPAppender + then make sure to set the LocationInfo option of that + appender as well. + + + + + + Actually do the writing of the xml + + the writer to use + the event to write + + + Generate XML that is compatible with the log4j schema. + + + + + + The version of the log4j schema to use. + + + + Only version 1.2 of the log4j schema is supported. + + + + + + The default object Renderer. + + + + The default renderer supports rendering objects and collections to strings. + + + See the method for details of the output. + + + Nicko Cadell + Gert Driesen + + + + Implement this interface in order to render objects as strings + + + + Certain types require special case conversion to + string form. This conversion is done by an object renderer. + Object renderers implement the + interface. + + + Nicko Cadell + Gert Driesen + + + + Render the object to a string + + The map used to lookup renderers + The object to render + The writer to render to + + + Render the object to a + string. + + + The parameter is + provided to lookup and render other objects. This is + very useful where contains + nested objects of unknown type. The + method can be used to render these objects. + + + + + + Default constructor + + + + Default constructor + + + + + + Render the object to a string + + The map used to lookup renderers + The object to render + The writer to render to + + + Render the object to a string. + + + The parameter is + provided to lookup and render other objects. This is + very useful where contains + nested objects of unknown type. The + method can be used to render these objects. + + + The default renderer supports rendering objects to strings as follows: + + + + Value + Rendered String + + + null + + "(null)" + + + + + + + For a one dimensional array this is the + array type name, an open brace, followed by a comma + separated list of the elements (using the appropriate + renderer), followed by a close brace. + + + For example: int[] {1, 2, 3}. + + + If the array is not one dimensional the + Array.ToString() is returned. + + + + + , & + + + Rendered as an open brace, followed by a comma + separated list of the elements (using the appropriate + renderer), followed by a close brace. + + + For example: {a, b, c}. + + + All collection classes that implement its subclasses, + or generic equivalents all implement the interface. + + + + + + + + Rendered as the key, an equals sign ('='), and the value (using the appropriate + renderer). + + + For example: key=value. + + + + + other + + Object.ToString() + + + + + + + + Render the array argument into a string + + The map used to lookup renderers + the array to render + The writer to render to + + + For a one dimensional array this is the + array type name, an open brace, followed by a comma + separated list of the elements (using the appropriate + renderer), followed by a close brace. For example: + int[] {1, 2, 3}. + + + If the array is not one dimensional the + Array.ToString() is returned. + + + + + + Render the enumerator argument into a string + + The map used to lookup renderers + the enumerator to render + The writer to render to + + + Rendered as an open brace, followed by a comma + separated list of the elements (using the appropriate + renderer), followed by a close brace. For example: + {a, b, c}. + + + + + + Render the DictionaryEntry argument into a string + + The map used to lookup renderers + the DictionaryEntry to render + The writer to render to + + + Render the key, an equals sign ('='), and the value (using the appropriate + renderer). For example: key=value. + + + + + + Map class objects to an . + + + + Maintains a mapping between types that require special + rendering and the that + is used to render them. + + + The method is used to render an + object using the appropriate renderers defined in this map. + + + Nicko Cadell + Gert Driesen + + + + Default Constructor + + + + Default constructor. + + + + + + Render using the appropriate renderer. + + the object to render to a string + the object rendered as a string + + + This is a convenience method used to render an object to a string. + The alternative method + should be used when streaming output to a . + + + + + + Render using the appropriate renderer. + + the object to render to a string + The writer to render to + + + Find the appropriate renderer for the type of the + parameter. This is accomplished by calling the + method. Once a renderer is found, it is + applied on the object and the result is returned + as a . + + + + + + Gets the renderer for the specified object type + + the object to lookup the renderer for + the renderer for + + + Gets the renderer for the specified object type. + + + Syntactic sugar method that calls + with the type of the object parameter. + + + + + + Gets the renderer for the specified type + + the type to lookup the renderer for + the renderer for the specified type + + + Returns the renderer for the specified type. + If no specific renderer has been defined the + will be returned. + + + + + + Internal function to recursively search interfaces + + the type to lookup the renderer for + the renderer for the specified type + + + + Clear the map of renderers + + + + Clear the custom renderers defined by using + . The + cannot be removed. + + + + + + Register an for . + + the type that will be rendered by + the renderer for + + + Register an object renderer for a specific source type. + This renderer will be returned from a call to + specifying the same as an argument. + + + + + + Get the default renderer instance + + the default renderer + + + Get the default renderer + + + + + + Interface implemented by logger repository plugins. + + + + Plugins define additional behavior that can be associated + with a . + The held by the + property is used to store the plugins for a repository. + + + The log4net.Config.PluginAttribute can be used to + attach plugins to repositories created using configuration + attributes. + + + Nicko Cadell + Gert Driesen + + + + Attaches the plugin to the specified . + + The that this plugin should be attached to. + + + A plugin may only be attached to a single repository. + + + This method is called when the plugin is attached to the repository. + + + + + + Is called when the plugin is to shutdown. + + + + This method is called to notify the plugin that + it should stop operating and should detach from + the repository. + + + + + + Gets the name of the plugin. + + + The name of the plugin. + + + + Plugins are stored in the + keyed by name. Each plugin instance attached to a + repository must be a unique name. + + + + + + A strongly-typed collection of objects. + + Nicko Cadell + + + + Creates a read-only wrapper for a PluginCollection instance. + + list to create a readonly wrapper arround + + A PluginCollection wrapper that is read-only. + + + + + Initializes a new instance of the PluginCollection class + that is empty and has the default initial capacity. + + + + + Initializes a new instance of the PluginCollection class + that has the specified initial capacity. + + + The number of elements that the new PluginCollection is initially capable of storing. + + + + + Initializes a new instance of the PluginCollection class + that contains elements copied from the specified PluginCollection. + + The PluginCollection whose elements are copied to the new collection. + + + + Initializes a new instance of the PluginCollection class + that contains elements copied from the specified array. + + The array whose elements are copied to the new list. + + + + Initializes a new instance of the PluginCollection class + that contains elements copied from the specified collection. + + The collection whose elements are copied to the new list. + + + + Allow subclasses to avoid our default constructors + + + + + + + Copies the entire PluginCollection to a one-dimensional + array. + + The one-dimensional array to copy to. + + + + Copies the entire PluginCollection to a one-dimensional + array, starting at the specified index of the target array. + + The one-dimensional array to copy to. + The zero-based index in at which copying begins. + + + + Adds a to the end of the PluginCollection. + + The to be added to the end of the PluginCollection. + The index at which the value has been added. + + + + Removes all elements from the PluginCollection. + + + + + Creates a shallow copy of the . + + A new with a shallow copy of the collection data. + + + + Determines whether a given is in the PluginCollection. + + The to check for. + true if is found in the PluginCollection; otherwise, false. + + + + Returns the zero-based index of the first occurrence of a + in the PluginCollection. + + The to locate in the PluginCollection. + + The zero-based index of the first occurrence of + in the entire PluginCollection, if found; otherwise, -1. + + + + + Inserts an element into the PluginCollection at the specified index. + + The zero-based index at which should be inserted. + The to insert. + + is less than zero + -or- + is equal to or greater than . + + + + + Removes the first occurrence of a specific from the PluginCollection. + + The to remove from the PluginCollection. + + The specified was not found in the PluginCollection. + + + + + Removes the element at the specified index of the PluginCollection. + + The zero-based index of the element to remove. + + is less than zero. + -or- + is equal to or greater than . + + + + + Returns an enumerator that can iterate through the PluginCollection. + + An for the entire PluginCollection. + + + + Adds the elements of another PluginCollection to the current PluginCollection. + + The PluginCollection whose elements should be added to the end of the current PluginCollection. + The new of the PluginCollection. + + + + Adds the elements of a array to the current PluginCollection. + + The array whose elements should be added to the end of the PluginCollection. + The new of the PluginCollection. + + + + Adds the elements of a collection to the current PluginCollection. + + The collection whose elements should be added to the end of the PluginCollection. + The new of the PluginCollection. + + + + Sets the capacity to the actual number of elements. + + + + + is less than zero. + -or- + is equal to or greater than . + + + + + is less than zero. + -or- + is equal to or greater than . + + + + + Gets the number of elements actually contained in the PluginCollection. + + + + + Gets a value indicating whether access to the collection is synchronized (thread-safe). + + true if access to the ICollection is synchronized (thread-safe); otherwise, false. + + + + Gets an object that can be used to synchronize access to the collection. + + + An object that can be used to synchronize access to the collection. + + + + + Gets or sets the at the specified index. + + + The at the specified index. + + The zero-based index of the element to get or set. + + is less than zero. + -or- + is equal to or greater than . + + + + + Gets a value indicating whether the collection has a fixed size. + + true if the collection has a fixed size; otherwise, false. The default is false. + + + + Gets a value indicating whether the IList is read-only. + + true if the collection is read-only; otherwise, false. The default is false. + + + + Gets or sets the number of elements the PluginCollection can contain. + + + The number of elements the PluginCollection can contain. + + + + + Supports type-safe iteration over a . + + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + + + Type visible only to our subclasses + Used to access protected constructor + + + + + + A value + + + + + Supports simple iteration over a . + + + + + + Initializes a new instance of the Enumerator class. + + + + + + Advances the enumerator to the next element in the collection. + + + true if the enumerator was successfully advanced to the next element; + false if the enumerator has passed the end of the collection. + + + The collection was modified after the enumerator was created. + + + + + Sets the enumerator to its initial position, before the first element in the collection. + + + + + Gets the current element in the collection. + + + The current element in the collection. + + + + + + + + Map of repository plugins. + + + + This class is a name keyed map of the plugins that are + attached to a repository. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + The repository that the plugins should be attached to. + + + Initialize a new instance of the class with a + repository that the plugins should be attached to. + + + + + + Adds a to the map. + + The to add to the map. + + + The will be attached to the repository when added. + + + If there already exists a plugin with the same name + attached to the repository then the old plugin will + be and replaced with + the new plugin. + + + + + + Removes a from the map. + + The to remove from the map. + + + Remove a specific plugin from this map. + + + + + + Gets a by name. + + The name of the to lookup. + + The from the map with the name specified, or + null if no plugin is found. + + + + Lookup a plugin by name. If the plugin is not found null + will be returned. + + + + + + Gets all possible plugins as a list of objects. + + All possible plugins as a list of objects. + + + Get a collection of all the plugins defined in this map. + + + + + + Base implementation of + + + + Default abstract implementation of the + interface. This base class can be used by implementors + of the interface. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + the name of the plugin + + Initializes a new Plugin with the specified name. + + + + + Attaches this plugin to a . + + The that this plugin should be attached to. + + + A plugin may only be attached to a single repository. + + + This method is called when the plugin is attached to the repository. + + + + + + Is called when the plugin is to shutdown. + + + + This method is called to notify the plugin that + it should stop operating and should detach from + the repository. + + + + + + The name of this plugin. + + + + + The repository this plugin is attached to. + + + + + Gets or sets the name of the plugin. + + + The name of the plugin. + + + + Plugins are stored in the + keyed by name. Each plugin instance attached to a + repository must be a unique name. + + + The name of the plugin must not change one the + plugin has been attached to a repository. + + + + + + The repository for this plugin + + + The that this plugin is attached to. + + + + Gets or sets the that this plugin is + attached to. + + + + + + Plugin that listens for events from the + + + + This plugin publishes an instance of + on a specified . This listens for logging events delivered from + a remote . + + + When an event is received it is relogged within the attached repository + as if it had been raised locally. + + + Nicko Cadell + Gert Driesen + + + + Default constructor + + + + Initializes a new instance of the class. + + + The property must be set. + + + + + + Construct with sink Uri. + + The name to publish the sink under in the remoting infrastructure. + See for more details. + + + Initializes a new instance of the class + with specified name. + + + + + + Attaches this plugin to a . + + The that this plugin should be attached to. + + + A plugin may only be attached to a single repository. + + + This method is called when the plugin is attached to the repository. + + + + + + Is called when the plugin is to shutdown. + + + + When the plugin is shutdown the remote logging + sink is disconnected. + + + + + + Gets or sets the URI of this sink. + + + The URI of this sink. + + + + This is the name under which the object is marshaled. + + + + + + + Delivers objects to a remote sink. + + + + Internal class used to listen for logging events + and deliver them to the local repository. + + + + + + Constructor + + The repository to log to. + + + Initializes a new instance of the for the + specified . + + + + + + Logs the events to the repository. + + The events to log. + + + The events passed are logged to the + + + + + + Obtains a lifetime service object to control the lifetime + policy for this instance. + + null to indicate that this instance should live forever. + + + Obtains a lifetime service object to control the lifetime + policy for this instance. This object should live forever + therefore this implementation returns null. + + + + + + The underlying that events should + be logged to. + + + + + Default implementation of + + + + This default implementation of the + interface is used to create the default subclass + of the object. + + + Nicko Cadell + Gert Driesen + + + + Interface abstracts creation of instances + + + + This interface is used by the to + create new objects. + + + The method is called + to create a named . + + + Implement this interface to create new subclasses of . + + + Nicko Cadell + Gert Driesen + + + + Create a new instance + + The name of the . + The instance for the specified name. + + + Create a new instance with the + specified name. + + + Called by the to create + new named instances. + + + If the is null then the root logger + must be returned. + + + + + + Default constructor + + + + Initializes a new instance of the class. + + + + + + Create a new instance + + The name of the . + The instance for the specified name. + + + Create a new instance with the + specified name. + + + Called by the to create + new named instances. + + + If the is null then the root logger + must be returned. + + + + + + Default internal subclass of + + + + This subclass has no additional behavior over the + class but does allow instances + to be created. + + + + + + Implementation of used by + + + + Internal class used to provide implementation of + interface. Applications should use to get + logger instances. + + + This is one of the central classes in the log4net implementation. One of the + distinctive features of log4net are hierarchical loggers and their + evaluation. The organizes the + instances into a rooted tree hierarchy. + + + The class is abstract. Only concrete subclasses of + can be created. The + is used to create instances of this type for the . + + + Nicko Cadell + Gert Driesen + Aspi Havewala + Douglas de la Torre + + + + This constructor created a new instance and + sets its name. + + The name of the . + + + This constructor is protected and designed to be used by + a subclass that is not abstract. + + + Loggers are constructed by + objects. See for the default + logger creator. + + + + + + Add to the list of appenders of this + Logger instance. + + An appender to add to this logger + + + Add to the list of appenders of this + Logger instance. + + + If is already in the list of + appenders, then it won't be added again. + + + + + + Look for the appender named as name + + The name of the appender to lookup + The appender with the name specified, or null. + + + Returns the named appender, or null if the appender is not found. + + + + + + Remove all previously added appenders from this Logger instance. + + + + Remove all previously added appenders from this Logger instance. + + + This is useful when re-reading configuration information. + + + + + + Remove the appender passed as parameter form the list of appenders. + + The appender to remove + The appender removed from the list + + + Remove the appender passed as parameter form the list of appenders. + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + Remove the appender passed as parameter form the list of appenders. + + The name of the appender to remove + The appender removed from the list + + + Remove the named appender passed as parameter form the list of appenders. + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + This generic form is intended to be used by wrappers. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The level of the message to be logged. + The message object to log. + The exception to log, including its stack trace. + + + Generate a logging event for the specified using + the and . + + + This method must not throw any exception to the caller. + + + + + + This is the most generic printing method that is intended to be used + by wrappers. + + The event being logged. + + + Logs the specified logging event through this logger. + + + This method must not throw any exception to the caller. + + + + + + Checks if this logger is enabled for a given passed as parameter. + + The level to check. + + true if this logger is enabled for level, otherwise false. + + + + Test if this logger is going to log events of the specified . + + + This method must not throw any exception to the caller. + + + + + + Deliver the to the attached appenders. + + The event to log. + + + Call the appenders in the hierarchy starting at + this. If no appenders could be found, emit a + warning. + + + This method calls all the appenders inherited from the + hierarchy circumventing any evaluation of whether to log or not + to log the particular log request. + + + + + + Closes all attached appenders implementing the interface. + + + + Used to ensure that the appenders are correctly shutdown. + + + + + + This is the most generic printing method. This generic form is intended to be used by wrappers + + The level of the message to be logged. + The message object to log. + The exception to log, including its stack trace. + + + Generate a logging event for the specified using + the . + + + + + + Creates a new logging event and logs the event without further checks. + + The declaring type of the method that is + the stack boundary into the logging system for this call. + The level of the message to be logged. + The message object to log. + The exception to log, including its stack trace. + + + Generates a logging event and delivers it to the attached + appenders. + + + + + + Creates a new logging event and logs the event without further checks. + + The event being logged. + + + Delivers the logging event to the attached appenders. + + + + + + The fully qualified type of the Logger class. + + + + + The name of this logger. + + + + + The assigned level of this logger. + + + + The level variable need not be + assigned a value in which case it is inherited + form the hierarchy. + + + + + + The parent of this logger. + + + + The parent of this logger. + All loggers have at least one ancestor which is the root logger. + + + + + + Loggers need to know what Hierarchy they are in. + + + + Loggers need to know what Hierarchy they are in. + The hierarchy that this logger is a member of is stored + here. + + + + + + Helper implementation of the interface + + + + + Flag indicating if child loggers inherit their parents appenders + + + + Additivity is set to true by default, that is children inherit + the appenders of their ancestors by default. If this variable is + set to false then the appenders found in the + ancestors of this logger are not used. However, the children + of this logger will inherit its appenders, unless the children + have their additivity flag set to false too. See + the user manual for more details. + + + + + + Lock to protect AppenderAttachedImpl variable m_appenderAttachedImpl + + + + + Gets or sets the parent logger in the hierarchy. + + + The parent logger in the hierarchy. + + + + Part of the Composite pattern that makes the hierarchy. + The hierarchy is parent linked rather than child linked. + + + + + + Gets or sets a value indicating if child loggers inherit their parent's appenders. + + + true if child loggers inherit their parent's appenders. + + + + Additivity is set to true by default, that is children inherit + the appenders of their ancestors by default. If this variable is + set to false then the appenders found in the + ancestors of this logger are not used. However, the children + of this logger will inherit its appenders, unless the children + have their additivity flag set to false too. See + the user manual for more details. + + + + + + Gets the effective level for this logger. + + The nearest level in the logger hierarchy. + + + Starting from this logger, searches the logger hierarchy for a + non-null level and returns it. Otherwise, returns the level of the + root logger. + + The Logger class is designed so that this method executes as + quickly as possible. + + + + + Gets or sets the where this + Logger instance is attached to. + + The hierarchy that this logger belongs to. + + + This logger must be attached to a single . + + + + + + Gets or sets the assigned , if any, for this Logger. + + + The of this logger. + + + + The assigned can be null. + + + + + + Get the appenders contained in this logger as an + . + + A collection of the appenders in this logger + + + Get the appenders contained in this logger as an + . If no appenders + can be found, then a is returned. + + + + + + Gets the logger name. + + + The name of the logger. + + + + The name of this logger + + + + + + Gets the where this + Logger instance is attached to. + + + The that this logger belongs to. + + + + Gets the where this + Logger instance is attached to. + + + + + + Construct a new Logger + + the name of the logger + + + Initializes a new instance of the class + with the specified name. + + + + + + Delegate used to handle logger creation event notifications. + + The in which the has been created. + The event args that hold the instance that has been created. + + + Delegate used to handle logger creation event notifications. + + + + + + Provides data for the event. + + + + A event is raised every time a + is created. + + + + + + The created + + + + + Constructor + + The that has been created. + + + Initializes a new instance of the event argument + class,with the specified . + + + + + + Gets the that has been created. + + + The that has been created. + + + + The that has been created. + + + + + + Hierarchical organization of loggers + + + + The casual user should not have to deal with this class + directly. + + + This class is specialized in retrieving loggers by name and + also maintaining the logger hierarchy. Implements the + interface. + + + The structure of the logger hierarchy is maintained by the + method. The hierarchy is such that children + link to their parent but parents do not have any references to their + children. Moreover, loggers can be instantiated in any order, in + particular descendant before ancestor. + + + In case a descendant is created before a particular ancestor, + then it creates a provision node for the ancestor and adds itself + to the provision node. Other descendants of the same ancestor add + themselves to the previously created provision node. + + + Nicko Cadell + Gert Driesen + + + + Base implementation of + + + + Default abstract implementation of the interface. + + + Skeleton implementation of the interface. + All types can extend this type. + + + Nicko Cadell + Gert Driesen + + + + Interface implemented by logger repositories. + + + + This interface is implemented by logger repositories. e.g. + . + + + This interface is used by the + to obtain interfaces. + + + Nicko Cadell + Gert Driesen + + + + Check if the named logger exists in the repository. If so return + its reference, otherwise returns null. + + The name of the logger to lookup + The Logger object with the name specified + + + If the names logger exists it is returned, otherwise + null is returned. + + + + + + Returns all the currently defined loggers as an Array. + + All the defined loggers + + + Returns all the currently defined loggers as an Array. + + + + + + Returns a named logger instance + + The name of the logger to retrieve + The logger object with the name specified + + + Returns a named logger instance. + + + If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated and + then linked with its existing ancestors as well as children. + + + + + Shutdown the repository + + + Shutting down a repository will safely close and remove + all appenders in all loggers including the root logger. + + + Some appenders need to be closed before the + application exists. Otherwise, pending logging events might be + lost. + + + The method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Reset the repositories configuration to a default state + + + + Reset all values contained in this instance to their + default state. + + + Existing loggers are not removed. They are just reset. + + + This method should be used sparingly and with care as it will + block all logging until it is completed. + + + + + + Log the through this repository. + + the event to log + + + This method should not normally be used to log. + The interface should be used + for routine logging. This interface can be obtained + using the method. + + + The logEvent is delivered to the appropriate logger and + that logger is then responsible for logging the event. + + + + + + Returns all the Appenders that are configured as an Array. + + All the Appenders + + + Returns all the Appenders that are configured as an Array. + + + + + + The name of the repository + + + The name of the repository + + + + The name of the repository. + + + + + + RendererMap accesses the object renderer map for this repository. + + + RendererMap accesses the object renderer map for this repository. + + + + RendererMap accesses the object renderer map for this repository. + + + The RendererMap holds a mapping between types and + objects. + + + + + + The plugin map for this repository. + + + The plugin map for this repository. + + + + The plugin map holds the instances + that have been attached to this repository. + + + + + + Get the level map for the Repository. + + + + Get the level map for the Repository. + + + The level map defines the mappings between + level names and objects in + this repository. + + + + + + The threshold for all events in this repository + + + The threshold for all events in this repository + + + + The threshold for all events in this repository. + + + + + + Flag indicates if this repository has been configured. + + + Flag indicates if this repository has been configured. + + + + Flag indicates if this repository has been configured. + + + + + + Event to notify that the repository has been shutdown. + + + Event to notify that the repository has been shutdown. + + + + Event raised when the repository has been shutdown. + + + + + + Event to notify that the repository has had its configuration reset. + + + Event to notify that the repository has had its configuration reset. + + + + Event raised when the repository's configuration has been + reset to default. + + + + + + Event to notify that the repository has had its configuration changed. + + + Event to notify that the repository has had its configuration changed. + + + + Event raised when the repository's configuration has been changed. + + + + + + Repository specific properties + + + Repository specific properties + + + + These properties can be specified on a repository specific basis. + + + + + + Default Constructor + + + + Initializes the repository with default (empty) properties. + + + + + + Construct the repository using specific properties + + the properties to set for this repository + + + Initializes the repository with specified properties. + + + + + + Test if logger exists + + The name of the logger to lookup + The Logger object with the name specified + + + Check if the named logger exists in the repository. If so return + its reference, otherwise returns null. + + + + + + Returns all the currently defined loggers in the repository + + All the defined loggers + + + Returns all the currently defined loggers in the repository as an Array. + + + + + + Return a new logger instance + + The name of the logger to retrieve + The logger object with the name specified + + + Return a new logger instance. + + + If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated and + then linked with its existing ancestors as well as children. + + + + + + Shutdown the repository + + + + Shutdown the repository. Can be overridden in a subclass. + This base class implementation notifies the + listeners and all attached plugins of the shutdown event. + + + + + + Reset the repositories configuration to a default state + + + + Reset all values contained in this instance to their + default state. + + + Existing loggers are not removed. They are just reset. + + + This method should be used sparingly and with care as it will + block all logging until it is completed. + + + + + + Log the logEvent through this repository. + + the event to log + + + This method should not normally be used to log. + The interface should be used + for routine logging. This interface can be obtained + using the method. + + + The logEvent is delivered to the appropriate logger and + that logger is then responsible for logging the event. + + + + + + Returns all the Appenders that are configured as an Array. + + All the Appenders + + + Returns all the Appenders that are configured as an Array. + + + + + + Adds an object renderer for a specific class. + + The type that will be rendered by the renderer supplied. + The object renderer used to render the object. + + + Adds an object renderer for a specific class. + + + + + + Notify the registered listeners that the repository is shutting down + + Empty EventArgs + + + Notify any listeners that this repository is shutting down. + + + + + + Notify the registered listeners that the repository has had its configuration reset + + Empty EventArgs + + + Notify any listeners that this repository's configuration has been reset. + + + + + + Notify the registered listeners that the repository has had its configuration changed + + Empty EventArgs + + + Notify any listeners that this repository's configuration has changed. + + + + + + Raise a configuration changed event on this repository + + EventArgs.Empty + + + Applications that programmatically change the configuration of the repository should + raise this event notification to notify listeners. + + + + + + The name of the repository + + + The string name of the repository + + + + The name of this repository. The name is + used to store and lookup the repositories + stored by the . + + + + + + The threshold for all events in this repository + + + The threshold for all events in this repository + + + + The threshold for all events in this repository + + + + + + RendererMap accesses the object renderer map for this repository. + + + RendererMap accesses the object renderer map for this repository. + + + + RendererMap accesses the object renderer map for this repository. + + + The RendererMap holds a mapping between types and + objects. + + + + + + The plugin map for this repository. + + + The plugin map for this repository. + + + + The plugin map holds the instances + that have been attached to this repository. + + + + + + Get the level map for the Repository. + + + + Get the level map for the Repository. + + + The level map defines the mappings between + level names and objects in + this repository. + + + + + + Flag indicates if this repository has been configured. + + + Flag indicates if this repository has been configured. + + + + Flag indicates if this repository has been configured. + + + + + + Event to notify that the repository has been shutdown. + + + Event to notify that the repository has been shutdown. + + + + Event raised when the repository has been shutdown. + + + + + + Event to notify that the repository has had its configuration reset. + + + Event to notify that the repository has had its configuration reset. + + + + Event raised when the repository's configuration has been + reset to default. + + + + + + Event to notify that the repository has had its configuration changed. + + + Event to notify that the repository has had its configuration changed. + + + + Event raised when the repository's configuration has been changed. + + + + + + Repository specific properties + + + Repository specific properties + + + These properties can be specified on a repository specific basis + + + + + Basic Configurator interface for repositories + + + + Interface used by basic configurator to configure a + with a default . + + + A should implement this interface to support + configuration by the . + + + Nicko Cadell + Gert Driesen + + + + Initialize the repository using the specified appender + + the appender to use to log all logging events + + + Configure the repository to route all logging events to the + specified appender. + + + + + + Configure repository using XML + + + + Interface used by Xml configurator to configure a . + + + A should implement this interface to support + configuration by the . + + + Nicko Cadell + Gert Driesen + + + + Initialize the repository using the specified config + + the element containing the root of the config + + + The schema for the XML configuration data is defined by + the implementation. + + + + + + Default constructor + + + + Initializes a new instance of the class. + + + + + + Construct with properties + + The properties to pass to this repository. + + + Initializes a new instance of the class. + + + + + + Construct with a logger factory + + The factory to use to create new logger instances. + + + Initializes a new instance of the class with + the specified . + + + + + + Construct with properties and a logger factory + + The properties to pass to this repository. + The factory to use to create new logger instances. + + + Initializes a new instance of the class with + the specified . + + + + + + Test if a logger exists + + The name of the logger to lookup + The Logger object with the name specified + + + Check if the named logger exists in the hierarchy. If so return + its reference, otherwise returns null. + + + + + + Returns all the currently defined loggers in the hierarchy as an Array + + All the defined loggers + + + Returns all the currently defined loggers in the hierarchy as an Array. + The root logger is not included in the returned + enumeration. + + + + + + Return a new logger instance named as the first parameter using + the default factory. + + + + Return a new logger instance named as the first parameter using + the default factory. + + + If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated and + then linked with its existing ancestors as well as children. + + + The name of the logger to retrieve + The logger object with the name specified + + + + Shutting down a hierarchy will safely close and remove + all appenders in all loggers including the root logger. + + + + Shutting down a hierarchy will safely close and remove + all appenders in all loggers including the root logger. + + + Some appenders need to be closed before the + application exists. Otherwise, pending logging events might be + lost. + + + The Shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Reset all values contained in this hierarchy instance to their default. + + + + Reset all values contained in this hierarchy instance to their + default. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set its default "off" value. + + + Existing loggers are not removed. They are just reset. + + + This method should be used sparingly and with care as it will + block all logging until it is completed. + + + + + + Log the logEvent through this hierarchy. + + the event to log + + + This method should not normally be used to log. + The interface should be used + for routine logging. This interface can be obtained + using the method. + + + The logEvent is delivered to the appropriate logger and + that logger is then responsible for logging the event. + + + + + + Returns all the Appenders that are currently configured + + An array containing all the currently configured appenders + + + Returns all the instances that are currently configured. + All the loggers are searched for appenders. The appenders may also be containers + for appenders and these are also searched for additional loggers. + + + The list returned is unordered but does not contain duplicates. + + + + + + Collect the appenders from an . + The appender may also be a container. + + + + + + + Collect the appenders from an container + + + + + + + Initialize the log4net system using the specified appender + + the appender to use to log all logging events + + + + Initialize the log4net system using the specified appender + + the appender to use to log all logging events + + + This method provides the same functionality as the + method implemented + on this object, but it is protected and therefore can be called by subclasses. + + + + + + Initialize the log4net system using the specified config + + the element containing the root of the config + + + + Initialize the log4net system using the specified config + + the element containing the root of the config + + + This method provides the same functionality as the + method implemented + on this object, but it is protected and therefore can be called by subclasses. + + + + + + Test if this hierarchy is disabled for the specified . + + The level to check against. + + true if the repository is disabled for the level argument, false otherwise. + + + + If this hierarchy has not been configured then this method will + always return true. + + + This method will return true if this repository is + disabled for level object passed as parameter and + false otherwise. + + + See also the property. + + + + + + Clear all logger definitions from the internal hashtable + + + + This call will clear all logger definitions from the internal + hashtable. Invoking this method will irrevocably mess up the + logger hierarchy. + + + You should really know what you are doing before + invoking this method. + + + + + + Return a new logger instance named as the first parameter using + . + + The name of the logger to retrieve + The factory that will make the new logger instance + The logger object with the name specified + + + If a logger of that name already exists, then it will be + returned. Otherwise, a new logger will be instantiated by the + parameter and linked with its existing + ancestors as well as children. + + + + + + Sends a logger creation event to all registered listeners + + The newly created logger + + Raises the logger creation event. + + + + + Updates all the parents of the specified logger + + The logger to update the parents for + + + This method loops through all the potential parents of + . There 3 possible cases: + + + + No entry for the potential parent of exists + + We create a ProvisionNode for this potential + parent and insert in that provision node. + + + + The entry is of type Logger for the potential parent. + + The entry is 's nearest existing parent. We + update 's parent field with this entry. We also break from + he loop because updating our parent's parent is our parent's + responsibility. + + + + The entry is of type ProvisionNode for this potential parent. + + We add to the list of children for this + potential parent. + + + + + + + + Replace a with a in the hierarchy. + + + + + + We update the links for all the children that placed themselves + in the provision node 'pn'. The second argument 'log' is a + reference for the newly created Logger, parent of all the + children in 'pn'. + + + We loop on all the children 'c' in 'pn'. + + + If the child 'c' has been already linked to a child of + 'log' then there is no need to update 'c'. + + + Otherwise, we set log's parent field to c's parent and set + c's parent field to log. + + + + + + Define or redefine a Level using the values in the argument + + the level values + + + Define or redefine a Level using the values in the argument + + + Supports setting levels via the configuration file. + + + + + + Set a Property using the values in the argument + + the property value + + + Set a Property using the values in the argument. + + + Supports setting property values via the configuration file. + + + + + + Event used to notify that a logger has been created. + + + + Event raised when a logger is created. + + + + + + Has no appender warning been emitted + + + + Flag to indicate if we have already issued a warning + about not having an appender warning. + + + + + + Get the root of this hierarchy + + + + Get the root of this hierarchy. + + + + + + Gets or sets the default instance. + + The default + + + The logger factory is used to create logger instances. + + + + + + A class to hold the value, name and display name for a level + + + + A class to hold the value, name and display name for a level + + + + + + Override Object.ToString to return sensible debug info + + string info about this object + + + + Value of the level + + + + If the value is not set (defaults to -1) the value will be looked + up for the current level with the same name. + + + + + + Name of the level + + + The name of the level + + + + The name of the level. + + + + + + Display name for the level + + + The display name of the level + + + + The display name of the level. + + + + + + A class to hold the key and data for a property set in the config file + + + + A class to hold the key and data for a property set in the config file + + + + + + Override Object.ToString to return sensible debug info + + string info about this object + + + + Property Key + + + Property Key + + + + Property Key. + + + + + + Property Value + + + Property Value + + + + Property Value. + + + + + + Used internally to accelerate hash table searches. + + + + Internal class used to improve performance of + string keyed hashtables. + + + The hashcode of the string is cached for reuse. + The string is stored as an interned value. + When comparing two objects for equality + the reference equality of the interned strings is compared. + + + Nicko Cadell + Gert Driesen + + + + Construct key with string name + + + + Initializes a new instance of the class + with the specified name. + + + Stores the hashcode of the string and interns + the string key to optimize comparisons. + + + The Compact Framework 1.0 the + method does not work. On the Compact Framework + the string keys are not interned nor are they + compared by reference. + + + The name of the logger. + + + + Returns a hash code for the current instance. + + A hash code for the current instance. + + + Returns the cached hashcode. + + + + + + Determines whether two instances + are equal. + + The to compare with the current . + + true if the specified is equal to the current ; otherwise, false. + + + + Compares the references of the interned strings. + + + + + + Provision nodes are used where no logger instance has been specified + + + + instances are used in the + when there is no specified + for that node. + + + A provision node holds a list of child loggers on behalf of + a logger that does not exist. + + + Nicko Cadell + Gert Driesen + + + + Create a new provision node with child node + + A child logger to add to this node. + + + Initializes a new instance of the class + with the specified child logger. + + + + + + The sits at the root of the logger hierarchy tree. + + + + The is a regular except + that it provides several guarantees. + + + First, it cannot be assigned a null + level. Second, since the root logger cannot have a parent, the + property always returns the value of the + level field without walking the hierarchy. + + + Nicko Cadell + Gert Driesen + + + + Construct a + + The level to assign to the root logger. + + + Initializes a new instance of the class with + the specified logging level. + + + The root logger names itself as "root". However, the root + logger cannot be retrieved by name. + + + + + + Gets the assigned level value without walking the logger hierarchy. + + The assigned level value without walking the logger hierarchy. + + + Because the root logger cannot have a parent and its level + must not be null this property just returns the + value of . + + + + + + Gets or sets the assigned for the root logger. + + + The of the root logger. + + + + Setting the level of the root logger to a null reference + may have catastrophic results. We prevent this here. + + + + + + Initializes the log4net environment using an XML DOM. + + + + Configures a using an XML DOM. + + + Nicko Cadell + Gert Driesen + + + + Construct the configurator for a hierarchy + + The hierarchy to build. + + + Initializes a new instance of the class + with the specified . + + + + + + Configure the hierarchy by parsing a DOM tree of XML elements. + + The root element to parse. + + + Configure the hierarchy by parsing a DOM tree of XML elements. + + + + + + Parse appenders by IDREF. + + The appender ref element. + The instance of the appender that the ref refers to. + + + Parse an XML element that represents an appender and return + the appender. + + + + + + Parses an appender element. + + The appender element. + The appender instance or null when parsing failed. + + + Parse an XML element that represents an appender and return + the appender instance. + + + + + + Parses a logger element. + + The logger element. + + + Parse an XML element that represents a logger. + + + + + + Parses the root logger element. + + The root element. + + + Parse an XML element that represents the root logger. + + + + + + Parses the children of a logger element. + + The category element. + The logger instance. + Flag to indicate if the logger is the root logger. + + + Parse the child elements of a <logger> element. + + + + + + Parses an object renderer. + + The renderer element. + + + Parse an XML element that represents a renderer. + + + + + + Parses a level element. + + The level element. + The logger object to set the level on. + Flag to indicate if the logger is the root logger. + + + Parse an XML element that represents a level. + + + + + + Sets a parameter on an object. + + The parameter element. + The object to set the parameter on. + + The parameter name must correspond to a writable property + on the object. The value of the parameter is a string, + therefore this function will attempt to set a string + property first. If unable to set a string property it + will inspect the property and its argument type. It will + attempt to call a static method called Parse on the + type of the property. This method will take a single + string argument and return a value that can be used to + set the property. + + + + + Test if an element has no attributes or child elements + + the element to inspect + true if the element has any attributes or child elements, false otherwise + + + + Test if a is constructible with Activator.CreateInstance. + + the type to inspect + true if the type is creatable using a default constructor, false otherwise + + + + Look for a method on the that matches the supplied + + the type that has the method + the name of the method + the method info found + + + The method must be a public instance method on the . + The method must be named or "Add" followed by . + The method must take a single parameter. + + + + + + Converts a string value to a target type. + + The type of object to convert the string to. + The string value to use as the value of the object. + + + An object of type with value or + null when the conversion could not be performed. + + + + + + Creates an object as specified in XML. + + The XML element that contains the definition of the object. + The object type to use if not explicitly specified. + The type that the returned object must be or must inherit from. + The object or null + + + Parse an XML element and create an object instance based on the configuration + data. + + + The type of the instance may be specified in the XML. If not + specified then the is used + as the type. However the type is specified it must support the + type. + + + + + + key: appenderName, value: appender. + + + + + The Hierarchy being configured. + + + + + Delegate used to handle logger repository shutdown event notifications + + The that is shutting down. + Empty event args + + + Delegate used to handle logger repository shutdown event notifications. + + + + + + Delegate used to handle logger repository configuration reset event notifications + + The that has had its configuration reset. + Empty event args + + + Delegate used to handle logger repository configuration reset event notifications. + + + + + + Delegate used to handle event notifications for logger repository configuration changes. + + The that has had its configuration changed. + Empty event arguments. + + + Delegate used to handle event notifications for logger repository configuration changes. + + + + + + Write the name of the current AppDomain to the output + + + + Write the name of the current AppDomain to the output writer + + + Nicko Cadell + + + + Write the name of the current AppDomain to the output + + the writer to write to + null, state is not set + + + Writes name of the current AppDomain to the output . + + + + + + Write the current date to the output + + + + Date pattern converter, uses a to format + the current date and time to the writer as a string. + + + The value of the determines + the formatting of the date. The following values are allowed: + + + Option value + Output + + + ISO8601 + + Uses the formatter. + Formats using the "yyyy-MM-dd HH:mm:ss,fff" pattern. + + + + DATE + + Uses the formatter. + Formats using the "dd MMM yyyy HH:mm:ss,fff" for example, "06 Nov 1994 15:49:37,459". + + + + ABSOLUTE + + Uses the formatter. + Formats using the "HH:mm:ss,fff" for example, "15:49:37,459". + + + + other + + Any other pattern string uses the formatter. + This formatter passes the pattern string to the + method. + For details on valid patterns see + DateTimeFormatInfo Class. + + + + + + The date and time is in the local time zone and is rendered in that zone. + To output the time in Universal time see . + + + Nicko Cadell + + + + The used to render the date to a string + + + + The used to render the date to a string + + + + + + Initialize the converter options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Write the current date to the output + + that will receive the formatted result. + null, state is not set + + + Pass the current date and time to the + for it to render it to the writer. + + + The date and time passed is in the local time zone. + + + + + + Write an environment variable to the output + + + + Write an environment variable to the output writer. + The value of the determines + the name of the variable to output. + + + Nicko Cadell + + + + Write an environment variable to the output + + the writer to write to + null, state is not set + + + Writes the environment variable to the output . + The name of the environment variable to output must be set + using the + property. + + + + + + Write the current thread identity to the output + + + + Write the current thread identity to the output writer + + + Nicko Cadell + + + + Write the current thread identity to the output + + the writer to write to + null, state is not set + + + Writes the current thread identity to the output . + + + + + + Pattern converter for literal string instances in the pattern + + + + Writes the literal string value specified in the + property to + the output. + + + Nicko Cadell + + + + Set the next converter in the chain + + The next pattern converter in the chain + The next pattern converter + + + Special case the building of the pattern converter chain + for instances. Two adjacent + literals in the pattern can be represented by a single combined + pattern converter. This implementation detects when a + is added to the chain + after this converter and combines its value with this converter's + literal value. + + + + + + Write the literal to the output + + the writer to write to + null, not set + + + Override the formatting behavior to ignore the FormattingInfo + because we have a literal instead. + + + Writes the value of + to the output . + + + + + + Convert this pattern into the rendered message + + that will receive the formatted result. + null, not set + + + This method is not used. + + + + + + Writes a newline to the output + + + + Writes the system dependent line terminator to the output. + This behavior can be overridden by setting the : + + + + Option Value + Output + + + DOS + DOS or Windows line terminator "\r\n" + + + UNIX + UNIX line terminator "\n" + + + + Nicko Cadell + + + + Initialize the converter + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Write the current process ID to the output + + + + Write the current process ID to the output writer + + + Nicko Cadell + + + + Write the current process ID to the output + + the writer to write to + null, state is not set + + + Write the current process ID to the output . + + + + + + Property pattern converter + + + + This pattern converter reads the thread and global properties. + The thread properties take priority over global properties. + See for details of the + thread properties. See for + details of the global properties. + + + If the is specified then that will be used to + lookup a single property. If no is specified + then all properties will be dumped as a list of key value pairs. + + + Nicko Cadell + + + + Write the property value to the output + + that will receive the formatted result. + null, state is not set + + + Writes out the value of a named property. The property name + should be set in the + property. + + + If the is set to null + then all the properties are written as key value pairs. + + + + + + A Pattern converter that generates a string of random characters + + + + The converter generates a string of random characters. By default + the string is length 4. This can be changed by setting the + to the string value of the length required. + + + The random characters in the string are limited to uppercase letters + and numbers only. + + + The random number generator used by this class is not cryptographically secure. + + + Nicko Cadell + + + + Shared random number generator + + + + + Length of random string to generate. Default length 4. + + + + + Initialize the converter options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Write a randoim string to the output + + the writer to write to + null, state is not set + + + Write a randoim string to the output . + + + + + + Write the current threads username to the output + + + + Write the current threads username to the output writer + + + Nicko Cadell + + + + Write the current threads username to the output + + the writer to write to + null, state is not set + + + Write the current threads username to the output . + + + + + + Write the UTC date time to the output + + + + Date pattern converter, uses a to format + the current date and time in Universal time. + + + See the for details on the date pattern syntax. + + + + Nicko Cadell + + + + Write the current date and time to the output + + that will receive the formatted result. + null, state is not set + + + Pass the current date and time to the + for it to render it to the writer. + + + The date is in Universal time when it is rendered. + + + + + + + Type converter for Boolean. + + + + Supports conversion from string to bool type. + + + + + + Nicko Cadell + Gert Driesen + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Convert the source object to the type supported by this object + + the object to convert + the converted object + + + Uses the method to convert the + argument to a . + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Exception base type for conversion errors. + + + + This type extends . It + does not add any new functionality but does differentiate the + type of exception being thrown. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Constructor + + A message to include with the exception. + + + Initializes a new instance of the class + with the specified message. + + + + + + Constructor + + A message to include with the exception. + A nested exception to include. + + + Initializes a new instance of the class + with the specified message and inner exception. + + + + + + Serialization constructor + + The that holds the serialized object data about the exception being thrown. + The that contains contextual information about the source or destination. + + + Initializes a new instance of the class + with serialized data. + + + + + + Creates a new instance of the class. + + The conversion destination type. + The value to convert. + An instance of the . + + + Creates a new instance of the class. + + + + + + Creates a new instance of the class. + + The conversion destination type. + The value to convert. + A nested exception to include. + An instance of the . + + + Creates a new instance of the class. + + + + + + Register of type converters for specific types. + + + + Maintains a registry of type converters used to convert between + types. + + + Use the and + methods to register new converters. + The and methods + lookup appropriate converters to use. + + + + + Nicko Cadell + Gert Driesen + + + + Private constructor + + + Initializes a new instance of the class. + + + + + Static constructor. + + + + This constructor defines the intrinsic type converters. + + + + + + Adds a converter for a specific type. + + The type being converted to. + The type converter to use to convert to the destination type. + + + Adds a converter instance for a specific type. + + + + + + Adds a converter for a specific type. + + The type being converted to. + The type of the type converter to use to convert to the destination type. + + + Adds a converter for a specific type. + + + + + + Gets the type converter to use to convert values to the destination type. + + The type being converted from. + The type being converted to. + + The type converter instance to use for type conversions or null + if no type converter is found. + + + + Gets the type converter to use to convert values to the destination type. + + + + + + Gets the type converter to use to convert values to the destination type. + + The type being converted to. + + The type converter instance to use for type conversions or null + if no type converter is found. + + + + Gets the type converter to use to convert values to the destination type. + + + + + + Lookups the type converter to use as specified by the attributes on the + destination type. + + The type being converted to. + + The type converter instance to use for type conversions or null + if no type converter is found. + + + + + Creates the instance of the type converter. + + The type of the type converter. + + The type converter instance to use for type conversions or null + if no type converter is found. + + + + The type specified for the type converter must implement + the or interfaces + and must have a public default (no argument) constructor. + + + + + + Mapping from to type converter. + + + + + Supports conversion from string to type. + + + + Supports conversion from string to type. + + + + + + Nicko Cadell + Gert Driesen + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Overrides the ConvertFrom method of IConvertFrom. + + the object to convert to an encoding + the encoding + + + Uses the method to + convert the argument to an . + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Interface supported by type converters + + + + This interface supports conversion from a single type to arbitrary types. + See . + + + Nicko Cadell + + + + Returns whether this converter can convert the object to the specified type + + A Type that represents the type you want to convert to + true if the conversion is possible + + + Test if the type supported by this converter can be converted to the + . + + + + + + Converts the given value object to the specified type, using the arguments + + the object to convert + The Type to convert the value parameter to + the converted object + + + Converts the (which must be of the type supported + by this converter) to the specified.. + + + + + + Supports conversion from string to type. + + + + Supports conversion from string to type. + + + + + Nicko Cadell + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Overrides the ConvertFrom method of IConvertFrom. + + the object to convert to an IPAddress + the IPAddress + + + Uses the method to convert the + argument to an . + If that fails then the string is resolved as a DNS hostname. + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Valid characters in an IPv4 or IPv6 address string. (Does not support subnets) + + + + + Supports conversion from string to type. + + + + Supports conversion from string to type. + + + The string is used as the + of the . + + + + + + Nicko Cadell + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Overrides the ConvertFrom method of IConvertFrom. + + the object to convert to a PatternLayout + the PatternLayout + + + Creates and returns a new using + the as the + . + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Convert between string and + + + + Supports conversion from string to type, + and from a type to a string. + + + The string is used as the + of the . + + + + + + Nicko Cadell + + + + Can the target type be converted to the type supported by this object + + A that represents the type you want to convert to + true if the conversion is possible + + + Returns true if the is + assignable from a type. + + + + + + Converts the given value object to the specified type, using the arguments + + the object to convert + The Type to convert the value parameter to + the converted object + + + Uses the method to convert the + argument to a . + + + + The object cannot be converted to the + . To check for this condition use the + method. + + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Overrides the ConvertFrom method of IConvertFrom. + + the object to convert to a PatternString + the PatternString + + + Creates and returns a new using + the as the + . + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Supports conversion from string to type. + + + + Supports conversion from string to type. + + + + + + Nicko Cadell + + + + Can the source type be converted to the type supported by this object + + the type to convert + true if the conversion is possible + + + Returns true if the is + the type. + + + + + + Overrides the ConvertFrom method of IConvertFrom. + + the object to convert to a Type + the Type + + + Uses the method to convert the + argument to a . + Additional effort is made to locate partially specified types + by searching the loaded assemblies. + + + + The object cannot be converted to the + target type. To check for this condition use the + method. + + + + + Attribute used to associate a type converter + + + + Class and Interface level attribute that specifies a type converter + to use with the associated type. + + + To associate a type converter with a target type apply a + TypeConverterAttribute to the target type. Specify the + type of the type converter on the attribute. + + + Nicko Cadell + Gert Driesen + + + + The string type name of the type converter + + + + + Default constructor + + + + Default constructor + + + + + + Create a new type converter attribute for the specified type name + + The string type name of the type converter + + + The type specified must implement the + or the interfaces. + + + + + + Create a new type converter attribute for the specified type + + The type of the type converter + + + The type specified must implement the + or the interfaces. + + + + + + The string type name of the type converter + + + The string type name of the type converter + + + + The type specified must implement the + or the interfaces. + + + + + + A straightforward implementation of the interface. + + + + This is the default implementation of the + interface. Implementors of the interface + should aggregate an instance of this type. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Append on on all attached appenders. + + The event being logged. + The number of appenders called. + + + Calls the method on all + attached appenders. + + + + + + Append on on all attached appenders. + + The array of events being logged. + The number of appenders called. + + + Calls the method on all + attached appenders. + + + + + + Calls the DoAppende method on the with + the objects supplied. + + The appender + The events + + + If the supports the + interface then the will be passed + through using that interface. Otherwise the + objects in the array will be passed one at a time. + + + + + + Attaches an appender. + + The appender to add. + + + If the appender is already in the list it won't be added again. + + + + + + Gets an attached appender with the specified name. + + The name of the appender to get. + + The appender with the name specified, or null if no appender with the + specified name is found. + + + + Lookup an attached appender by name. + + + + + + Removes all attached appenders. + + + + Removes and closes all attached appenders + + + + + + Removes the specified appender from the list of attached appenders. + + The appender to remove. + The appender removed from the list + + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + Removes the appender with the specified name from the list of appenders. + + The name of the appender to remove. + The appender removed from the list + + + The appender removed is not closed. + If you are discarding the appender you must call + on the appender removed. + + + + + + List of appenders + + + + + Array of appenders, used to cache the m_appenderList + + + + + Gets all attached appenders. + + + A collection of attached appenders, or null if there + are no attached appenders. + + + + The read only collection of all currently attached appenders. + + + + + + This class aggregates several PropertiesDictionary collections together. + + + + Provides a dictionary style lookup over an ordered list of + collections. + + + Nicko Cadell + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Add a Properties Dictionary to this composite collection + + the properties to add + + + Properties dictionaries added first take precedence over dictionaries added + later. + + + + + + Flatten this composite collection into a single properties dictionary + + the flattened dictionary + + + Reduces the collection of ordered dictionaries to a single dictionary + containing the resultant values for the keys. + + + + + + Gets the value of a property + + + The value for the property with the specified key + + + + Looks up the value for the specified. + The collections are searched + in the order in which they were added to this collection. The value + returned is the value held by the first collection that contains + the specified key. + + + If none of the collections contain the specified key then + null is returned. + + + + + + Base class for Context Properties implementations + + + + This class defines a basic property get set accessor + + + Nicko Cadell + + + + Gets or sets the value of a property + + + The value for the property with the specified key + + + + Gets or sets the value of a property + + + + + + Subclass of that maintains a count of + the number of bytes written. + + + + This writer counts the number of bytes written. + + + Nicko Cadell + Gert Driesen + + + + that does not leak exceptions + + + + does not throw exceptions when things go wrong. + Instead, it delegates error handling to its . + + + Nicko Cadell + Gert Driesen + + + + Adapter that extends and forwards all + messages to an instance of . + + + + Adapter that extends and forwards all + messages to an instance of . + + + Nicko Cadell + + + + The writer to forward messages to + + + + + Create an instance of that forwards all + messages to a . + + The to forward to + + + Create an instance of that forwards all + messages to a . + + + + + + Closes the writer and releases any system resources associated with the writer + + + + + + + + + Dispose this writer + + flag indicating if we are being disposed + + + Dispose this writer + + + + + + Flushes any buffered output + + + + Clears all buffers for the writer and causes any buffered data to be written + to the underlying device + + + + + + Writes a character to the wrapped TextWriter + + the value to write to the TextWriter + + + Writes a character to the wrapped TextWriter + + + + + + Writes a character buffer to the wrapped TextWriter + + the data buffer + the start index + the number of characters to write + + + Writes a character buffer to the wrapped TextWriter + + + + + + Writes a string to the wrapped TextWriter + + the value to write to the TextWriter + + + Writes a string to the wrapped TextWriter + + + + + + Gets or sets the underlying . + + + The underlying . + + + + Gets or sets the underlying . + + + + + + The Encoding in which the output is written + + + The + + + + The Encoding in which the output is written + + + + + + Gets an object that controls formatting + + + The format provider + + + + Gets an object that controls formatting + + + + + + Gets or sets the line terminator string used by the TextWriter + + + The line terminator to use + + + + Gets or sets the line terminator string used by the TextWriter + + + + + + Constructor + + the writer to actually write to + the error handler to report error to + + + Create a new QuietTextWriter using a writer and error handler + + + + + + Writes a character to the underlying writer + + the char to write + + + Writes a character to the underlying writer + + + + + + Writes a buffer to the underlying writer + + the buffer to write + the start index to write from + the number of characters to write + + + Writes a buffer to the underlying writer + + + + + + Writes a string to the output. + + The string data to write to the output. + + + Writes a string to the output. + + + + + + Closes the underlying output writer. + + + + Closes the underlying output writer. + + + + + + The error handler instance to pass all errors to + + + + + Flag to indicate if this writer is closed + + + + + Gets or sets the error handler that all errors are passed to. + + + The error handler that all errors are passed to. + + + + Gets or sets the error handler that all errors are passed to. + + + + + + Gets a value indicating whether this writer is closed. + + + true if this writer is closed, otherwise false. + + + + Gets a value indicating whether this writer is closed. + + + + + + Constructor + + The to actually write to. + The to report errors to. + + + Creates a new instance of the class + with the specified and . + + + + + + Writes a character to the underlying writer and counts the number of bytes written. + + the char to write + + + Overrides implementation of . Counts + the number of bytes written. + + + + + + Writes a buffer to the underlying writer and counts the number of bytes written. + + the buffer to write + the start index to write from + the number of characters to write + + + Overrides implementation of . Counts + the number of bytes written. + + + + + + Writes a string to the output and counts the number of bytes written. + + The string data to write to the output. + + + Overrides implementation of . Counts + the number of bytes written. + + + + + + Total number of bytes written. + + + + + Gets or sets the total number of bytes written. + + + The total number of bytes written. + + + + Gets or sets the total number of bytes written. + + + + + + A fixed size rolling buffer of logging events. + + + + An array backed fixed size leaky bucket. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + The maximum number of logging events in the buffer. + + + Initializes a new instance of the class with + the specified maximum number of buffered logging events. + + + The argument is not a positive integer. + + + + Appends a to the buffer. + + The event to append to the buffer. + The event discarded from the buffer, if the buffer is full, otherwise null. + + + Append an event to the buffer. If the buffer still contains free space then + null is returned. If the buffer is full then an event will be dropped + to make space for the new event, the event dropped is returned. + + + + + + Get and remove the oldest event in the buffer. + + The oldest logging event in the buffer + + + Gets the oldest (first) logging event in the buffer and removes it + from the buffer. + + + + + + Pops all the logging events from the buffer into an array. + + An array of all the logging events in the buffer. + + + Get all the events in the buffer and clear the buffer. + + + + + + Clear the buffer + + + + Clear the buffer of all events. The events in the buffer are lost. + + + + + + Gets the th oldest event currently in the buffer. + + The th oldest event currently in the buffer. + + + If is outside the range 0 to the number of events + currently in the buffer, then null is returned. + + + + + + Gets the maximum size of the buffer. + + The maximum size of the buffer. + + + Gets the maximum size of the buffer + + + + + + Gets the number of logging events in the buffer. + + The number of logging events in the buffer. + + + This number is guaranteed to be in the range 0 to + (inclusive). + + + + + + An always empty . + + + + A singleton implementation of the + interface that always represents an empty collection. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to enforce the singleton pattern. + + + + + + Copies the elements of the to an + , starting at a particular Array index. + + The one-dimensional + that is the destination of the elements copied from + . The Array must have zero-based + indexing. + The zero-based index in array at which + copying begins. + + + As the collection is empty no values are copied into the array. + + + + + + Returns an enumerator that can iterate through a collection. + + + An that can be used to + iterate through the collection. + + + + As the collection is empty a is returned. + + + + + + The singleton instance of the empty collection. + + + + + Gets the singleton instance of the empty collection. + + The singleton instance of the empty collection. + + + Gets the singleton instance of the empty collection. + + + + + + Gets a value indicating if access to the is synchronized (thread-safe). + + + true if access to the is synchronized (thread-safe); otherwise, false. + + + + For the this property is always true. + + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + As the collection is empty the is always 0. + + + + + + Gets an object that can be used to synchronize access to the . + + + An object that can be used to synchronize access to the . + + + + As the collection is empty and thread safe and synchronized this instance is also + the object. + + + + + + An always empty . + + + + A singleton implementation of the + interface that always represents an empty collection. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to enforce the singleton pattern. + + + + + + Copies the elements of the to an + , starting at a particular Array index. + + The one-dimensional + that is the destination of the elements copied from + . The Array must have zero-based + indexing. + The zero-based index in array at which + copying begins. + + + As the collection is empty no values are copied into the array. + + + + + + Returns an enumerator that can iterate through a collection. + + + An that can be used to + iterate through the collection. + + + + As the collection is empty a is returned. + + + + + + Adds an element with the provided key and value to the + . + + The to use as the key of the element to add. + The to use as the value of the element to add. + + + As the collection is empty no new values can be added. A + is thrown if this method is called. + + + This dictionary is always empty and cannot be modified. + + + + Removes all elements from the . + + + + As the collection is empty no values can be removed. A + is thrown if this method is called. + + + This dictionary is always empty and cannot be modified. + + + + Determines whether the contains an element + with the specified key. + + The key to locate in the . + false + + + As the collection is empty the method always returns false. + + + + + + Returns an enumerator that can iterate through a collection. + + + An that can be used to + iterate through the collection. + + + + As the collection is empty a is returned. + + + + + + Removes the element with the specified key from the . + + The key of the element to remove. + + + As the collection is empty no values can be removed. A + is thrown if this method is called. + + + This dictionary is always empty and cannot be modified. + + + + The singleton instance of the empty dictionary. + + + + + Gets the singleton instance of the . + + The singleton instance of the . + + + Gets the singleton instance of the . + + + + + + Gets a value indicating if access to the is synchronized (thread-safe). + + + true if access to the is synchronized (thread-safe); otherwise, false. + + + + For the this property is always true. + + + + + + Gets the number of elements contained in the + + + The number of elements contained in the . + + + + As the collection is empty the is always 0. + + + + + + Gets an object that can be used to synchronize access to the . + + + An object that can be used to synchronize access to the . + + + + As the collection is empty and thread safe and synchronized this instance is also + the object. + + + + + + Gets a value indicating whether the has a fixed size. + + true + + + As the collection is empty always returns true. + + + + + + Gets a value indicating whether the is read-only. + + true + + + As the collection is empty always returns true. + + + + + + Gets an containing the keys of the . + + An containing the keys of the . + + + As the collection is empty a is returned. + + + + + + Gets an containing the values of the . + + An containing the values of the . + + + As the collection is empty a is returned. + + + + + + Gets or sets the element with the specified key. + + The key of the element to get or set. + null + + + As the collection is empty no values can be looked up or stored. + If the index getter is called then null is returned. + A is thrown if the setter is called. + + + This dictionary is always empty and cannot be modified. + + + + Contain the information obtained when parsing formatting modifiers + in conversion modifiers. + + + + Holds the formatting information extracted from the format string by + the . This is used by the + objects when rendering the output. + + + Nicko Cadell + Gert Driesen + + + + Defaut Constructor + + + + Initializes a new instance of the class. + + + + + + Constructor + + + + Initializes a new instance of the class + with the specified parameters. + + + + + + Gets or sets the minimum value. + + + The minimum value. + + + + Gets or sets the minimum value. + + + + + + Gets or sets the maximum value. + + + The maximum value. + + + + Gets or sets the maximum value. + + + + + + Gets or sets a flag indicating whether left align is enabled + or not. + + + A flag indicating whether left align is enabled or not. + + + + Gets or sets a flag indicating whether left align is enabled or not. + + + + + + Implementation of Properties collection for the + + + + This class implements a properties collection that is thread safe and supports both + storing properties and capturing a read only copy of the current propertied. + + + This class is optimized to the scenario where the properties are read frequently + and are modified infrequently. + + + Nicko Cadell + + + + The read only copy of the properties. + + + + This variable is declared volatile to prevent the compiler and JIT from + reordering reads and writes of this thread performed on different threads. + + + + + + Lock object used to synchronize updates within this instance + + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Remove a property from the global context + + the key for the entry to remove + + + Removing an entry from the global context properties is relatively expensive compared + with reading a value. + + + + + + Clear the global context properties + + + + + Get a readonly immutable copy of the properties + + the current global context properties + + + This implementation is fast because the GlobalContextProperties class + stores a readonly copy of the properties. + + + + + + Gets or sets the value of a property + + + The value for the property with the specified key + + + + Reading the value for a key is faster than setting the value. + When the value is written a new read only copy of + the properties is created. + + + + + + Manages a mapping from levels to + + + + Manages an ordered mapping from instances + to subclasses. + + + Nicko Cadell + + + + Default constructor + + + + Initialise a new instance of . + + + + + + Add a to this mapping + + the entry to add + + + If a has previously been added + for the same then that entry will be + overwritten. + + + + + + Lookup the mapping for the specified level + + the level to lookup + the for the level or null if no mapping found + + + Lookup the value for the specified level. Finds the nearest + mapping value for the level that is equal to or less than the + specified. + + + If no mapping could be found then null is returned. + + + + + + Initialize options + + + + Caches the sorted list of in an array + + + + + + Implementation of Properties collection for the + + + + Class implements a collection of properties that is specific to each thread. + The class is not synchronized as each thread has its own . + + + Nicko Cadell + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Remove a property + + the key for the entry to remove + + + Remove the value for the specified from the context. + + + + + + Clear all the context properties + + + + Clear all the context properties + + + + + + Get the PropertiesDictionary stored in the LocalDataStoreSlot for this thread. + + create the dictionary if it does not exist, otherwise return null if is does not exist + the properties for this thread + + + The collection returned is only to be used on the calling thread. If the + caller needs to share the collection between different threads then the + caller must clone the collection before doings so. + + + + + + Gets or sets the value of a property + + + The value for the property with the specified key + + + + Get or set the property value for the specified. + + + + + + Outputs log statements from within the log4net assembly. + + + + Log4net components cannot make log4net logging calls. However, it is + sometimes useful for the user to learn about what log4net is + doing. + + + All log4net internal debug calls go to the standard output stream + whereas internal error messages are sent to the standard error output + stream. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + Static constructor that initializes logging by reading + settings from the application configuration file. + + + + The log4net.Internal.Debug application setting + controls internal debugging. This setting should be set + to true to enable debugging. + + + The log4net.Internal.Quiet application setting + suppresses all internal logging including error messages. + This setting should be set to true to enable message + suppression. + + + + + + Writes log4net internal debug messages to the + standard output stream. + + The message to log. + + + All internal debug messages are prepended with + the string "log4net: ". + + + + + + Writes log4net internal debug messages to the + standard output stream. + + The message to log. + An exception to log. + + + All internal debug messages are prepended with + the string "log4net: ". + + + + + + Writes log4net internal warning messages to the + standard error stream. + + The message to log. + + + All internal warning messages are prepended with + the string "log4net:WARN ". + + + + + + Writes log4net internal warning messages to the + standard error stream. + + The message to log. + An exception to log. + + + All internal warning messages are prepended with + the string "log4net:WARN ". + + + + + + Writes log4net internal error messages to the + standard error stream. + + The message to log. + + + All internal error messages are prepended with + the string "log4net:ERROR ". + + + + + + Writes log4net internal error messages to the + standard error stream. + + The message to log. + An exception to log. + + + All internal debug messages are prepended with + the string "log4net:ERROR ". + + + + + + Writes output to the standard output stream. + + The message to log. + + + Writes to both Console.Out and System.Diagnostics.Trace. + Note that the System.Diagnostics.Trace is not supported + on the Compact Framework. + + + If the AppDomain is not configured with a config file then + the call to System.Diagnostics.Trace may fail. This is only + an issue if you are programmatically creating your own AppDomains. + + + + + + Writes output to the standard error stream. + + The message to log. + + + Writes to both Console.Error and System.Diagnostics.Trace. + Note that the System.Diagnostics.Trace is not supported + on the Compact Framework. + + + If the AppDomain is not configured with a config file then + the call to System.Diagnostics.Trace may fail. This is only + an issue if you are programmatically creating your own AppDomains. + + + + + + Default debug level + + + + + In quietMode not even errors generate any output. + + + + + Gets or sets a value indicating whether log4net internal logging + is enabled or disabled. + + + true if log4net internal logging is enabled, otherwise + false. + + + + When set to true, internal debug level logging will be + displayed. + + + This value can be set by setting the application setting + log4net.Internal.Debug in the application configuration + file. + + + The default value is false, i.e. debugging is + disabled. + + + + + The following example enables internal debugging using the + application configuration file : + + + + + + + + + + + + + Gets or sets a value indicating whether log4net should generate no output + from internal logging, not even for errors. + + + true if log4net should generate no output at all from internal + logging, otherwise false. + + + + When set to true will cause internal logging at all levels to be + suppressed. This means that no warning or error reports will be logged. + This option overrides the setting and + disables all debug also. + + This value can be set by setting the application setting + log4net.Internal.Quiet in the application configuration file. + + + The default value is false, i.e. internal logging is not + disabled. + + + + The following example disables internal logging using the + application configuration file : + + + + + + + + + + + + Test if LogLog.Debug is enabled for output. + + + true if Debug is enabled + + + + Test if LogLog.Debug is enabled for output. + + + + + + Test if LogLog.Warn is enabled for output. + + + true if Warn is enabled + + + + Test if LogLog.Warn is enabled for output. + + + + + + Test if LogLog.Error is enabled for output. + + + true if Error is enabled + + + + Test if LogLog.Error is enabled for output. + + + + + + Represents a native error code and message. + + + + Represents a Win32 platform native error. + + + Nicko Cadell + Gert Driesen + + + + Create an instance of the class with the specified + error number and message. + + The number of the native error. + The message of the native error. + + + Create an instance of the class with the specified + error number and message. + + + + + + Create a new instance of the class for the last Windows error. + + + An instance of the class for the last windows error. + + + + The message for the error number is lookup up using the + native Win32 FormatMessage function. + + + + + + Create a new instance of the class. + + the error number for the native error + + An instance of the class for the specified + error number. + + + + The message for the specified error number is lookup up using the + native Win32 FormatMessage function. + + + + + + Retrieves the message corresponding with a Win32 message identifier. + + Message identifier for the requested message. + + The message corresponding with the specified message identifier. + + + + The message will be searched for in system message-table resource(s) + using the native FormatMessage function. + + + + + + Return error information string + + error information string + + + Return error information string + + + + + + Formats a message string. + + Formatting options, and how to interpret the parameter. + Location of the message definition. + Message identifier for the requested message. + Language identifier for the requested message. + If includes FORMAT_MESSAGE_ALLOCATE_BUFFER, the function allocates a buffer using the LocalAlloc function, and places the pointer to the buffer at the address specified in . + If the FORMAT_MESSAGE_ALLOCATE_BUFFER flag is not set, this parameter specifies the maximum number of TCHARs that can be stored in the output buffer. If FORMAT_MESSAGE_ALLOCATE_BUFFER is set, this parameter specifies the minimum number of TCHARs to allocate for an output buffer. + Pointer to an array of values that are used as insert values in the formatted message. + + + The function requires a message definition as input. The message definition can come from a + buffer passed into the function. It can come from a message table resource in an + already-loaded module. Or the caller can ask the function to search the system's message + table resource(s) for the message definition. The function finds the message definition + in a message table resource based on a message identifier and a language identifier. + The function copies the formatted message text to an output buffer, processing any embedded + insert sequences if requested. + + + To prevent the usage of unsafe code, this stub does not support inserting values in the formatted message. + + + + + If the function succeeds, the return value is the number of TCHARs stored in the output + buffer, excluding the terminating null character. + + + If the function fails, the return value is zero. To get extended error information, + call . + + + + + + Gets the number of the native error. + + + The number of the native error. + + + + Gets the number of the native error. + + + + + + Gets the message of the native error. + + + The message of the native error. + + + + + Gets the message of the native error. + + + + + An always empty . + + + + A singleton implementation of the over a collection + that is empty and not modifiable. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to enforce the singleton pattern. + + + + + + Test if the enumerator can advance, if so advance. + + false as the cannot advance. + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will always return false. + + + + + + Resets the enumerator back to the start. + + + + As the enumerator is over an empty collection does nothing. + + + + + + The singleton instance of the . + + + + + Gets the singleton instance of the . + + The singleton instance of the . + + + Gets the singleton instance of the . + + + + + + Gets the current object from the enumerator. + + + Throws an because the + never has a current value. + + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will throw an . + + + The collection is empty and + cannot be positioned over a valid location. + + + + Gets the current key from the enumerator. + + + Throws an exception because the + never has a current value. + + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will throw an . + + + The collection is empty and + cannot be positioned over a valid location. + + + + Gets the current value from the enumerator. + + The current value from the enumerator. + + Throws an because the + never has a current value. + + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will throw an . + + + The collection is empty and + cannot be positioned over a valid location. + + + + Gets the current entry from the enumerator. + + + Throws an because the + never has a current entry. + + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will throw an . + + + The collection is empty and + cannot be positioned over a valid location. + + + + An always empty . + + + + A singleton implementation of the over a collection + that is empty and not modifiable. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to enforce the singleton pattern. + + + + + + Test if the enumerator can advance, if so advance + + false as the cannot advance. + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will always return false. + + + + + + Resets the enumerator back to the start. + + + + As the enumerator is over an empty collection does nothing. + + + + + + The singleton instance of the . + + + + + Get the singleton instance of the . + + The singleton instance of the . + + + Gets the singleton instance of the . + + + + + + Gets the current object from the enumerator. + + + Throws an because the + never has a current value. + + + + As the enumerator is over an empty collection its + value cannot be moved over a valid position, therefore + will throw an . + + + The collection is empty and + cannot be positioned over a valid location. + + + + A SecurityContext used when a SecurityContext is not required + + + + The is a no-op implementation of the + base class. It is used where a + is required but one has not been provided. + + + Nicko Cadell + + + + Singleton instance of + + + + Singleton instance of + + + + + + Private constructor + + + + Private constructor for singleton pattern. + + + + + + Impersonate this SecurityContext + + State supplied by the caller + null + + + No impersonation is done and null is always returned. + + + + + + Implements log4net's default error handling policy which consists + of emitting a message for the first error in an appender and + ignoring all subsequent errors. + + + + The error message is printed on the standard error output stream. + + + This policy aims at protecting an otherwise working application + from being flooded with error messages when logging fails. + + + Nicko Cadell + Gert Driesen + + + + Default Constructor + + + + Initializes a new instance of the class. + + + + + + Constructor + + The prefix to use for each message. + + + Initializes a new instance of the class + with the specified prefix. + + + + + + Log an Error + + The error message. + The exception. + The internal error code. + + + Prints the message and the stack trace of the exception on the standard + error output stream. + + + + + + Log an Error + + The error message. + The exception. + + + Prints the message and the stack trace of the exception on the standard + error output stream. + + + + + + Log an error + + The error message. + + + Print a the error message passed as parameter on the standard + error output stream. + + + + + + Flag to indicate if it is the first error + + + + + String to prefix each message with + + + + + Is error logging enabled + + + + Is error logging enabled. Logging is only enabled for the + first error delivered to the . + + + + + + A convenience class to convert property values to specific types. + + + + Utility functions for converting types and parsing values. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + Converts a string to a value. + + String to convert. + The default value. + The value of . + + + If is "true", then true is returned. + If is "false", then false is returned. + Otherwise, is returned. + + + + + + Parses a file size into a number. + + String to parse. + The default value. + The value of . + + + Parses a file size of the form: number[KB|MB|GB] into a + long value. It is scaled with the appropriate multiplier. + + + is returned when + cannot be converted to a value. + + + + + + Converts a string to an object. + + The target type to convert to. + The string to convert to an object. + + The object converted from a string or null when the + conversion failed. + + + + Converts a string to an object. Uses the converter registry to try + to convert the string value into the specified target type. + + + + + + Checks if there is an appropriate type conversion from the source type to the target type. + + The type to convert from. + The type to convert to. + true if there is a conversion from the source type to the target type. + + Checks if there is an appropriate type conversion from the source type to the target type. + + + + + + + Converts an object to the target type. + + The object to convert to the target type. + The type to convert to. + The converted object. + + + Converts an object to the target type. + + + + + + Instantiates an object given a class name. + + The fully qualified class name of the object to instantiate. + The class to which the new object should belong. + The object to return in case of non-fulfillment. + + An instance of the or + if the object could not be instantiated. + + + + Checks that the is a subclass of + . If that test fails or the object could + not be instantiated, then is returned. + + + + + + Performs variable substitution in string from the + values of keys found in . + + The string on which variable substitution is performed. + The dictionary to use to lookup variables. + The result of the substitutions. + + + The variable substitution delimiters are ${ and }. + + + For example, if props contains key=value, then the call + + + + string s = OptionConverter.SubstituteVariables("Value of key is ${key}."); + + + + will set the variable s to "Value of key is value.". + + + If no value could be found for the specified key, then substitution + defaults to an empty string. + + + For example, if system properties contains no value for the key + "nonExistentKey", then the call + + + + string s = OptionConverter.SubstituteVariables("Value of nonExistentKey is [${nonExistentKey}]"); + + + + will set s to "Value of nonExistentKey is []". + + + An Exception is thrown if contains a start + delimiter "${" which is not balanced by a stop delimiter "}". + + + + + + Converts the string representation of the name or numeric value of one or + more enumerated constants to an equivalent enumerated object. + + The type to convert to. + The enum string value. + If true, ignore case; otherwise, regard case. + An object of type whose value is represented by . + + + + Most of the work of the class + is delegated to the PatternParser class. + + + + The PatternParser processes a pattern string and + returns a chain of objects. + + + Nicko Cadell + Gert Driesen + + + + Constructor + + The pattern to parse. + + + Initializes a new instance of the class + with the specified pattern string. + + + + + + Parses the pattern into a chain of pattern converters. + + The head of a chain of pattern converters. + + + Parses the pattern into a chain of pattern converters. + + + + + + Build the unified cache of converters from the static and instance maps + + the list of all the converter names + + + Build the unified cache of converters from the static and instance maps + + + + + + Internal method to parse the specified pattern to find specified matches + + the pattern to parse + the converter names to match in the pattern + + + The matches param must be sorted such that longer strings come before shorter ones. + + + + + + Process a parsed literal + + the literal text + + + + Process a parsed converter pattern + + the name of the converter + the optional option for the converter + the formatting info for the converter + + + + Resets the internal state of the parser and adds the specified pattern converter + to the chain. + + The pattern converter to add. + + + + The first pattern converter in the chain + + + + + the last pattern converter in the chain + + + + + The pattern + + + + + Internal map of converter identifiers to converter types + + + + This map overrides the static s_globalRulesRegistry map. + + + + + + Get the converter registry used by this parser + + + The converter registry used by this parser + + + + Get the converter registry used by this parser + + + + + + Sort strings by length + + + + that orders strings by string length. + The longest strings are placed first + + + + + + This class implements a patterned string. + + + + This string has embedded patterns that are resolved and expanded + when the string is formatted. + + + This class functions similarly to the + in that it accepts a pattern and renders it to a string. Unlike the + however the PatternString + does not render the properties of a specific but + of the process in general. + + + The recognized conversion pattern names are: + + + + Conversion Pattern Name + Effect + + + appdomain + + + Used to output the friendly name of the current AppDomain. + + + + + date + + + Used to output the date of the logging event in the local time zone. + To output the date in universal time use the %utcdate pattern. + The date conversion + specifier may be followed by a date format specifier enclosed + between braces. For example, %date{HH:mm:ss,fff} or + %date{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is + given then ISO8601 format is + assumed (). + + + The date format specifier admits the same syntax as the + time pattern string of the . + + + For better results it is recommended to use the log4net date + formatters. These can be specified using one of the strings + "ABSOLUTE", "DATE" and "ISO8601" for specifying + , + and respectively + . For example, + %date{ISO8601} or %date{ABSOLUTE}. + + + These dedicated date formatters perform significantly + better than . + + + + + env + + + Used to output the a specific environment variable. The key to + lookup must be specified within braces and directly following the + pattern specifier, e.g. %env{COMPUTERNAME} would include the value + of the COMPUTERNAME environment variable. + + + The env pattern is not supported on the .NET Compact Framework. + + + + + identity + + + Used to output the user name for the currently active user + (Principal.Identity.Name). + + + + + newline + + + Outputs the platform dependent line separator character or + characters. + + + This conversion pattern name offers the same performance as using + non-portable line separator strings such as "\n", or "\r\n". + Thus, it is the preferred way of specifying a line separator. + + + + + processid + + + Used to output the system process ID for the current process. + + + + + property + + + Used to output a specific context property. The key to + lookup must be specified within braces and directly following the + pattern specifier, e.g. %property{user} would include the value + from the property that is keyed by the string 'user'. Each property value + that is to be included in the log must be specified separately. + Properties are stored in logging contexts. By default + the log4net:HostName property is set to the name of machine on + which the event was originally logged. + + + If no key is specified, e.g. %property then all the keys and their + values are printed in a comma separated list. + + + The properties of an event are combined from a number of different + contexts. These are listed below in the order in which they are searched. + + + + the thread properties + + The that are set on the current + thread. These properties are shared by all events logged on this thread. + + + + the global properties + + The that are set globally. These + properties are shared by all the threads in the AppDomain. + + + + + + + random + + + Used to output a random string of characters. The string is made up of + uppercase letters and numbers. By default the string is 4 characters long. + The length of the string can be specified within braces directly following the + pattern specifier, e.g. %random{8} would output an 8 character string. + + + + + username + + + Used to output the WindowsIdentity for the currently + active user. + + + + + utcdate + + + Used to output the date of the logging event in universal time. + The date conversion + specifier may be followed by a date format specifier enclosed + between braces. For example, %utcdate{HH:mm:ss,fff} or + %utcdate{dd MMM yyyy HH:mm:ss,fff}. If no date format specifier is + given then ISO8601 format is + assumed (). + + + The date format specifier admits the same syntax as the + time pattern string of the . + + + For better results it is recommended to use the log4net date + formatters. These can be specified using one of the strings + "ABSOLUTE", "DATE" and "ISO8601" for specifying + , + and respectively + . For example, + %utcdate{ISO8601} or %utcdate{ABSOLUTE}. + + + These dedicated date formatters perform significantly + better than . + + + + + % + + + The sequence %% outputs a single percent sign. + + + + + + Additional pattern converters may be registered with a specific + instance using or + . + + + See the for details on the + format modifiers supported by the patterns. + + + Nicko Cadell + + + + Internal map of converter identifiers to converter types. + + + + + the pattern + + + + + the head of the pattern converter chain + + + + + patterns defined on this PatternString only + + + + + Initialize the global registry + + + + + Default constructor + + + + Initialize a new instance of + + + + + + Constructs a PatternString + + The pattern to use with this PatternString + + + Initialize a new instance of with the pattern specified. + + + + + + Initialize object options + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + + + + Create the used to parse the pattern + + the pattern to parse + The + + + Returns PatternParser used to parse the conversion string. Subclasses + may override this to return a subclass of PatternParser which recognize + custom conversion pattern name. + + + + + + Produces a formatted string as specified by the conversion pattern. + + The TextWriter to write the formatted event to + + + Format the pattern to the . + + + + + + Format the pattern as a string + + the pattern formatted as a string + + + Format the pattern to a string. + + + + + + Add a converter to this PatternString + + the converter info + + + This version of the method is used by the configurator. + Programmatic users should use the alternative method. + + + + + + Add a converter to this PatternString + + the name of the conversion pattern for this converter + the type of the converter + + + Add a converter to this PatternString + + + + + + Gets or sets the pattern formatting string + + + The pattern formatting string + + + + The ConversionPattern option. This is the string which + controls formatting and consists of a mix of literal content and + conversion specifiers. + + + + + + Wrapper class used to map converter names to converter types + + + + Wrapper class used to map converter names to converter types + + + + + + default constructor + + + + + Gets or sets the name of the conversion pattern + + + The name of the conversion pattern + + + + Gets or sets the name of the conversion pattern + + + + + + Gets or sets the type of the converter + + + The type of the converter + + + + Gets or sets the type of the converter + + + + + + String keyed object map. + + + + While this collection is serializable only member + objects that are serializable will + be serialized along with this collection. + + + Nicko Cadell + Gert Driesen + + + + String keyed object map that is read only. + + + + This collection is readonly and cannot be modified. + + + While this collection is serializable only member + objects that are serializable will + be serialized along with this collection. + + + Nicko Cadell + Gert Driesen + + + + The Hashtable used to store the properties data + + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Copy Constructor + + properties to copy + + + Initializes a new instance of the class. + + + + + + Deserialization constructor + + The that holds the serialized object data. + The that contains contextual information about the source or destination. + + + Initializes a new instance of the class + with serialized data. + + + + + + Gets the key names. + + An array of all the keys. + + + Gets the key names. + + + + + + Test if the dictionary contains a specified key + + the key to look for + true if the dictionary contains the specified key + + + Test if the dictionary contains a specified key + + + + + + Serializes this object into the provided. + + The to populate with data. + The destination for this serialization. + + + Serializes this object into the provided. + + + + + + See + + + + + See + + + + + + See + + + + + + + Remove all properties from the properties collection + + + + + See + + + + + + + See + + + + + + + See + + + + + Gets or sets the value of the property with the specified key. + + + The value of the property with the specified key. + + The key of the property to get or set. + + + The property value will only be serialized if it is serializable. + If it cannot be serialized it will be silently ignored if + a serialization operation is performed. + + + + + + The hashtable used to store the properties + + + The internal collection used to store the properties + + + + The hashtable used to store the properties + + + + + + See + + + + + See + + + + + See + + + + + See + + + + + See + + + + + See + + + + + The number of properties in this collection + + + + + See + + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Constructor + + properties to copy + + + Initializes a new instance of the class. + + + + + + Initializes a new instance of the class + with serialized data. + + The that holds the serialized object data. + The that contains contextual information about the source or destination. + + + Because this class is sealed the serialization constructor is private. + + + + + + Remove the entry with the specified key from this dictionary + + the key for the entry to remove + + + Remove the entry with the specified key from this dictionary + + + + + + See + + an enumerator + + + Returns a over the contest of this collection. + + + + + + See + + the key to remove + + + Remove the entry with the specified key from this dictionary + + + + + + See + + the key to lookup in the collection + true if the collection contains the specified key + + + Test if this collection contains a specified key. + + + + + + Remove all properties from the properties collection + + + + Remove all properties from the properties collection + + + + + + See + + the key + the value to store for the key + + + Store a value for the specified . + + + Thrown if the is not a string + + + + See + + + + + + + See + + + + + Gets or sets the value of the property with the specified key. + + + The value of the property with the specified key. + + The key of the property to get or set. + + + The property value will only be serialized if it is serializable. + If it cannot be serialized it will be silently ignored if + a serialization operation is performed. + + + + + + See + + + false + + + + This collection is modifiable. This property always + returns false. + + + + + + See + + + The value for the key specified. + + + + Get or set a value for the specified . + + + Thrown if the is not a string + + + + See + + + + + See + + + + + See + + + + + See + + + + + See + + + + + A that ignores the message + + + + This writer is used in special cases where it is necessary + to protect a writer from being closed by a client. + + + Nicko Cadell + + + + Constructor + + the writer to actually write to + + + Create a new ProtectCloseTextWriter using a writer + + + + + + Attach this instance to a different underlying + + the writer to attach to + + + Attach this instance to a different underlying + + + + + + Does not close the underlying output writer. + + + + Does not close the underlying output writer. + This method does nothing. + + + + + + Defines a lock that supports single writers and multiple readers + + + + ReaderWriterLock is used to synchronize access to a resource. + At any given time, it allows either concurrent read access for + multiple threads, or write access for a single thread. In a + situation where a resource is changed infrequently, a + ReaderWriterLock provides better throughput than a simple + one-at-a-time lock, such as . + + + If a platform does not support a System.Threading.ReaderWriterLock + implementation then all readers and writers are serialized. Therefore + the caller must not rely on multiple simultaneous readers. + + + Nicko Cadell + + + + Constructor + + + + Initializes a new instance of the class. + + + + + + Acquires a reader lock + + + + blocks if a different thread has the writer + lock, or if at least one thread is waiting for the writer lock. + + + + + + Decrements the lock count + + + + decrements the lock count. When the count + reaches zero, the lock is released. + + + + + + Acquires the writer lock + + + + This method blocks if another thread has a reader lock or writer lock. + + + + + + Decrements the lock count on the writer lock + + + + ReleaseWriterLock decrements the writer lock count. + When the count reaches zero, the writer lock is released. + + + + + + A that can be and reused + + + + A that can be and reused. + This uses a single buffer for string operations. + + + Nicko Cadell + + + + Create an instance of + + the format provider to use + + + Create an instance of + + + + + + Override Dispose to prevent closing of writer + + flag + + + Override Dispose to prevent closing of writer + + + + + + Reset this string writer so that it can be reused. + + the maximum buffer capacity before it is trimmed + the default size to make the buffer + + + Reset this string writer so that it can be reused. + The internal buffers are cleared and reset. + + + + + + Utility class for system specific information. + + + + Utility class of static methods for system specific information. + + + Nicko Cadell + Gert Driesen + Alexey Solofnenko + + + + Private constructor to prevent instances. + + + + Only static methods are exposed from this type. + + + + + + Initialize default values for private static fields. + + + + Only static methods are exposed from this type. + + + + + + Gets the assembly location path for the specified assembly. + + The assembly to get the location for. + The location of the assembly. + + + This method does not guarantee to return the correct path + to the assembly. If only tries to give an indication as to + where the assembly was loaded from. + + + + + + Gets the fully qualified name of the , including + the name of the assembly from which the was + loaded. + + The to get the fully qualified name for. + The fully qualified name for the . + + + This is equivalent to the Type.AssemblyQualifiedName property, + but this method works on the .NET Compact Framework 1.0 as well as + the full .NET runtime. + + + + + + Gets the short name of the . + + The to get the name for. + The short name of the . + + + The short name of the assembly is the + without the version, culture, or public key. i.e. it is just the + assembly's file name without the extension. + + + Use this rather than Assembly.GetName().Name because that + is not available on the Compact Framework. + + + Because of a FileIOPermission security demand we cannot do + the obvious Assembly.GetName().Name. We are allowed to get + the of the assembly so we + start from there and strip out just the assembly name. + + + + + + Gets the file name portion of the , including the extension. + + The to get the file name for. + The file name of the assembly. + + + Gets the file name portion of the , including the extension. + + + + + + Loads the type specified in the type string. + + A sibling type to use to load the type. + The name of the type to load. + Flag set to true to throw an exception if the type cannot be loaded. + true to ignore the case of the type name; otherwise, false + The type loaded or null if it could not be loaded. + + + If the type name is fully qualified, i.e. if contains an assembly name in + the type name, the type will be loaded from the system using + . + + + If the type name is not fully qualified, it will be loaded from the assembly + containing the specified relative type. If the type is not found in the assembly + then all the loaded assemblies will be searched for the type. + + + + + + Loads the type specified in the type string. + + The name of the type to load. + Flag set to true to throw an exception if the type cannot be loaded. + true to ignore the case of the type name; otherwise, false + The type loaded or null if it could not be loaded. + + + If the type name is fully qualified, i.e. if contains an assembly name in + the type name, the type will be loaded from the system using + . + + + If the type name is not fully qualified it will be loaded from the + assembly that is directly calling this method. If the type is not found + in the assembly then all the loaded assemblies will be searched for the type. + + + + + + Loads the type specified in the type string. + + An assembly to load the type from. + The name of the type to load. + Flag set to true to throw an exception if the type cannot be loaded. + true to ignore the case of the type name; otherwise, false + The type loaded or null if it could not be loaded. + + + If the type name is fully qualified, i.e. if contains an assembly name in + the type name, the type will be loaded from the system using + . + + + If the type name is not fully qualified it will be loaded from the specified + assembly. If the type is not found in the assembly then all the loaded assemblies + will be searched for the type. + + + + + + Generate a new guid + + A new Guid + + + Generate a new guid + + + + + + Create an + + The name of the parameter that caused the exception + The value of the argument that causes this exception + The message that describes the error + the ArgumentOutOfRangeException object + + + Create a new instance of the class + with a specified error message, the parameter name, and the value + of the argument. + + + The Compact Framework does not support the 3 parameter constructor for the + type. This method provides an + implementation that works for all platforms. + + + + + + Parse a string into an value + + the string to parse + out param where the parsed value is placed + true if the string was able to be parsed into an integer + + + Attempts to parse the string into an integer. If the string cannot + be parsed then this method returns false. The method does not throw an exception. + + + + + + Parse a string into an value + + the string to parse + out param where the parsed value is placed + true if the string was able to be parsed into an integer + + + Attempts to parse the string into an integer. If the string cannot + be parsed then this method returns false. The method does not throw an exception. + + + + + + Lookup an application setting + + the application settings key to lookup + the value for the key, or null + + + Configuration APIs are not supported under the Compact Framework + + + + + + Convert a path into a fully qualified local file path. + + The path to convert. + The fully qualified path. + + + Converts the path specified to a fully + qualified path. If the path is relative it is + taken as relative from the application base + directory. + + + The path specified must be a local file path, a URI is not supported. + + + + + + Creates a new case-insensitive instance of the class with the default initial capacity. + + A new case-insensitive instance of the class with the default initial capacity + + + The new Hashtable instance uses the default load factor, the CaseInsensitiveHashCodeProvider, and the CaseInsensitiveComparer. + + + + + + Gets an empty array of types. + + + + The Type.EmptyTypes field is not available on + the .NET Compact Framework 1.0. + + + + + + Cache the host name for the current machine + + + + + Cache the application friendly name + + + + + Text to output when a null is encountered. + + + + + Text to output when an unsupported feature is requested. + + + + + Start time for the current process. + + + + + Gets the system dependent line terminator. + + + The system dependent line terminator. + + + + Gets the system dependent line terminator. + + + + + + Gets the base directory for this . + + The base directory path for the current . + + + Gets the base directory for this . + + + The value returned may be either a local file path or a URI. + + + + + + Gets the path to the configuration file for the current . + + The path to the configuration file for the current . + + + The .NET Compact Framework 1.0 does not have a concept of a configuration + file. For this runtime, we use the entry assembly location as the root for + the configuration file name. + + + The value returned may be either a local file path or a URI. + + + + + + Gets the path to the file that first executed in the current . + + The path to the entry assembly. + + + Gets the path to the file that first executed in the current . + + + + + + Gets the ID of the current thread. + + The ID of the current thread. + + + On the .NET framework, the AppDomain.GetCurrentThreadId method + is used to obtain the thread ID for the current thread. This is the + operating system ID for the thread. + + + On the .NET Compact Framework 1.0 it is not possible to get the + operating system thread ID for the current thread. The native method + GetCurrentThreadId is implemented inline in a header file + and cannot be called. + + + On the .NET Framework 2.0 the Thread.ManagedThreadId is used as this + gives a stable id unrelated to the operating system thread ID which may + change if the runtime is using fibers. + + + + + + Get the host name or machine name for the current machine + + + The hostname or machine name + + + + Get the host name or machine name for the current machine + + + The host name () or + the machine name (Environment.MachineName) for + the current machine, or if neither of these are available + then NOT AVAILABLE is returned. + + + + + + Get this application's friendly name + + + The friendly name of this application as a string + + + + If available the name of the application is retrieved from + the AppDomain using AppDomain.CurrentDomain.FriendlyName. + + + Otherwise the file name of the entry assembly is used. + + + + + + Get the start time for the current process. + + + + This is the time at which the log4net library was loaded into the + AppDomain. Due to reports of a hang in the call to System.Diagnostics.Process.StartTime + this is not the start time for the current process. + + + The log4net library should be loaded by an application early during its + startup, therefore this start time should be a good approximation for + the actual start time. + + + Note that AppDomains may be loaded and unloaded within the + same process without the process terminating, however this start time + will be set per AppDomain. + + + + + + Text to output when a null is encountered. + + + + Use this value to indicate a null has been encountered while + outputting a string representation of an item. + + + The default value is (null). This value can be overridden by specifying + a value for the log4net.NullText appSetting in the application's + .config file. + + + + + + Text to output when an unsupported feature is requested. + + + + Use this value when an unsupported feature is requested. + + + The default value is NOT AVAILABLE. This value can be overridden by specifying + a value for the log4net.NotAvailableText appSetting in the application's + .config file. + + + + + + Utility class that represents a format string. + + + + Utility class that represents a format string. + + + Nicko Cadell + + + + Initialise the + + An that supplies culture-specific formatting information. + A containing zero or more format items. + An array containing zero or more objects to format. + + + + Format the string and arguments + + the formatted string + + + + Replaces the format item in a specified with the text equivalent + of the value of a corresponding instance in a specified array. + A specified parameter supplies culture-specific formatting information. + + An that supplies culture-specific formatting information. + A containing zero or more format items. + An array containing zero or more objects to format. + + A copy of format in which the format items have been replaced by the + equivalent of the corresponding instances of in args. + + + + This method does not throw exceptions. If an exception thrown while formatting the result the + exception and arguments are returned in the result string. + + + + + + Process an error during StringFormat + + + + + Dump the contents of an array into a string builder + + + + + Dump an object to a string + + + + + Implementation of Properties collection for the + + + + Class implements a collection of properties that is specific to each thread. + The class is not synchronized as each thread has its own . + + + Nicko Cadell + + + + The thread local data slot to use to store a PropertiesDictionary. + + + + + Internal constructor + + + + Initializes a new instance of the class. + + + + + + Remove a property + + the key for the entry to remove + + + Remove a property + + + + + + Clear all properties + + + + Clear all properties + + + + + + Get the PropertiesDictionary for this thread. + + create the dictionary if it does not exist, otherwise return null if is does not exist + the properties for this thread + + + The collection returned is only to be used on the calling thread. If the + caller needs to share the collection between different threads then the + caller must clone the collection before doing so. + + + + + + Gets or sets the value of a property + + + The value for the property with the specified key + + + + Gets or sets the value of a property + + + + + + Implementation of Stack for the + + + + Implementation of Stack for the + + + Nicko Cadell + + + + The stack store. + + + + + Internal constructor + + + + Initializes a new instance of the class. + + + + + + Clears all the contextual information held in this stack. + + + + Clears all the contextual information held in this stack. + Only call this if you think that this tread is being reused after + a previous call execution which may not have completed correctly. + You do not need to use this method if you always guarantee to call + the method of the + returned from even in exceptional circumstances, + for example by using the using(log4net.ThreadContext.Stacks["NDC"].Push("Stack_Message")) + syntax. + + + + + + Removes the top context from this stack. + + The message in the context that was removed from the top of this stack. + + + Remove the top context from this stack, and return + it to the caller. If this stack is empty then an + empty string (not ) is returned. + + + + + + Pushes a new context message into this stack. + + The new context message. + + An that can be used to clean up the context stack. + + + + Pushes a new context onto this stack. An + is returned that can be used to clean up this stack. This + can be easily combined with the using keyword to scope the + context. + + + Simple example of using the Push method with the using keyword. + + using(log4net.ThreadContext.Stacks["NDC"].Push("Stack_Message")) + { + log.Warn("This should have an ThreadContext Stack message"); + } + + + + + + Gets the current context information for this stack. + + The current context information. + + + + Gets the current context information for this stack. + + Gets the current context information + + + Gets the current context information for this stack. + + + + + + Get a portable version of this object + + the portable instance of this object + + + Get a cross thread portable version of this object + + + + + + The number of messages in the stack + + + The current number of messages in the stack + + + + The current number of messages in the stack. That is + the number of times has been called + minus the number of times has been called. + + + + + + Gets and sets the internal stack used by this + + The internal storage stack + + + This property is provided only to support backward compatability + of the . Tytpically the internal stack should not + be modified. + + + + + + Inner class used to represent a single context frame in the stack. + + + + Inner class used to represent a single context frame in the stack. + + + + + + Constructor + + The message for this context. + The parent context in the chain. + + + Initializes a new instance of the class + with the specified message and parent context. + + + + + + Get the message. + + The message. + + + Get the message. + + + + + + Gets the full text of the context down to the root level. + + + The full text of the context down to the root level. + + + + Gets the full text of the context down to the root level. + + + + + + Struct returned from the method. + + + + This struct implements the and is designed to be used + with the pattern to remove the stack frame at the end of the scope. + + + + + + The ThreadContextStack internal stack + + + + + The depth to trim the stack to when this instance is disposed + + + + + Constructor + + The internal stack used by the ThreadContextStack. + The depth to return the stack to when this object is disposed. + + + Initializes a new instance of the class with + the specified stack and return depth. + + + + + + Returns the stack to the correct depth. + + + + Returns the stack to the correct depth. + + + + + + Implementation of Stacks collection for the + + + + Implementation of Stacks collection for the + + + Nicko Cadell + + + + Internal constructor + + + + Initializes a new instance of the class. + + + + + + Gets the named thread context stack + + + The named stack + + + + Gets the named thread context stack + + + + + + Utility class for transforming strings. + + + + Utility class for transforming strings. + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + Write a string to an + + the writer to write to + the string to write + The string to replace non XML compliant chars with + + + The test is escaped either using XML escape entities + or using CDATA sections. + + + + + + Replace invalid XML characters in text string + + the XML text input string + the string to use in place of invalid characters + A string that does not contain invalid XML characters. + + + Certain Unicode code points are not allowed in the XML InfoSet, for + details see: http://www.w3.org/TR/REC-xml/#charsets. + + + This method replaces any illegal characters in the input string + with the mask string specified. + + + + + + Count the number of times that the substring occurs in the text + + the text to search + the substring to find + the number of times the substring occurs in the text + + + The substring is assumed to be non repeating within itself. + + + + + + Impersonate a Windows Account + + + + This impersonates a Windows account. + + + How the impersonation is done depends on the value of . + This allows the context to either impersonate a set of user credentials specified + using username, domain name and password or to revert to the process credentials. + + + + + + Default constructor + + + + Default constructor + + + + + + Initialize the SecurityContext based on the options set. + + + + This is part of the delayed object + activation scheme. The method must + be called on this object after the configuration properties have + been set. Until is called this + object is in an undefined state and must not be used. + + + If any of the configuration properties are modified then + must be called again. + + + The security context will try to Logon the specified user account and + capture a primary token for impersonation. + + + The required , + or properties were not specified. + + + + Impersonate the Windows account specified by the and properties. + + caller provided state + + An instance that will revoke the impersonation of this SecurityContext + + + + Depending on the property either + impersonate a user using credentials supplied or revert + to the process credentials. + + + + + + Create a given the userName, domainName and password. + + the user name + the domain name + the password + the for the account specified + + + Uses the Windows API call LogonUser to get a principal token for the account. This + token is used to initialize the WindowsIdentity. + + + + + + Gets or sets the impersonation mode for this security context + + + The impersonation mode for this security context + + + + Impersonate either a user with user credentials or + revert this thread to the credentials of the process. + The value is one of the + enum. + + + The default value is + + + When the mode is set to + the user's credentials are established using the + , and + values. + + + When the mode is set to + no other properties need to be set. If the calling thread is + impersonating then it will be reverted back to the process credentials. + + + + + + Gets or sets the Windows username for this security context + + + The Windows username for this security context + + + + This property must be set if + is set to (the default setting). + + + + + + Gets or sets the Windows domain name for this security context + + + The Windows domain name for this security context + + + + The default value for is the local machine name + taken from the property. + + + This property must be set if + is set to (the default setting). + + + + + + Sets the password for the Windows account specified by the and properties. + + + The password for the Windows account specified by the and properties. + + + + This property must be set if + is set to (the default setting). + + + + + + The impersonation modes for the + + + + See the property for + details. + + + + + + Impersonate a user using the credentials supplied + + + + + Revert this the thread to the credentials of the process + + + + + Adds to + + + + Helper class to expose the + through the interface. + + + + + + Constructor + + the impersonation context being wrapped + + + Constructor + + + + + + Revert the impersonation + + + + Revert the impersonation + + + + + + The log4net Global Context. + + + + The GlobalContext provides a location for global debugging + information to be stored. + + + The global context has a properties map and these properties can + be included in the output of log messages. The + supports selecting and outputing these properties. + + + By default the log4net:HostName property is set to the name of + the current machine. + + + + + GlobalContext.Properties["hostname"] = Environment.MachineName; + + + + Nicko Cadell + + + + Private Constructor. + + + Uses a private access modifier to prevent instantiation of this class. + + + + + The global context properties instance + + + + + The global properties map. + + + The global properties map. + + + + The global properties map. + + + + + + The log4net Logical Thread Context. + + + + The LogicalThreadContext provides a location for specific debugging + information to be stored. + The LogicalThreadContext properties override any or + properties with the same name. + + + The Logical Thread Context has a properties map and a stack. + The properties and stack can + be included in the output of log messages. The + supports selecting and outputting these properties. + + + The Logical Thread Context provides a diagnostic context for the current call context. + This is an instrument for distinguishing interleaved log + output from different sources. Log output is typically interleaved + when a server handles multiple clients near-simultaneously. + + + The Logical Thread Context is managed on a per basis. + + + Example of using the thread context properties to store a username. + + LogicalThreadContext.Properties["user"] = userName; + log.Info("This log message has a LogicalThreadContext Property called 'user'"); + + + Example of how to push a message into the context stack + + using(LogicalThreadContext.Stacks["LDC"].Push("my context message")) + { + log.Info("This log message has a LogicalThreadContext Stack message that includes 'my context message'"); + + } // at the end of the using block the message is automatically popped + + + + Nicko Cadell + + + + Private Constructor. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + The thread context properties instance + + + + + The thread context stacks instance + + + + + The thread properties map + + + The thread properties map + + + + The LogicalThreadContext properties override any + or properties with the same name. + + + + + + The thread stacks + + + stack map + + + + The logical thread stacks. + + + + + + This class is used by client applications to request logger instances. + + + + This class has static methods that are used by a client to request + a logger instance. The method is + used to retrieve a logger. + + + See the interface for more details. + + + Simple example of logging messages + + ILog log = LogManager.GetLogger("application-log"); + + log.Info("Application Start"); + log.Debug("This is a debug message"); + + if (log.IsDebugEnabled) + { + log.Debug("This is another debug message"); + } + + + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + Uses a private access modifier to prevent instantiation of this class. + + + + Returns the named logger if it exists. + + Returns the named logger if it exists. + + + + If the named logger exists (in the default repository) then it + returns a reference to the logger, otherwise it returns null. + + + The fully qualified logger name to look for. + The logger found, or null if no logger could be found. + + + + Returns the named logger if it exists. + + + + If the named logger exists (in the specified repository) then it + returns a reference to the logger, otherwise it returns + null. + + + The repository to lookup in. + The fully qualified logger name to look for. + + The logger found, or null if the logger doesn't exist in the specified + repository. + + + + + Returns the named logger if it exists. + + + + If the named logger exists (in the repository for the specified assembly) then it + returns a reference to the logger, otherwise it returns + null. + + + The assembly to use to lookup the repository. + The fully qualified logger name to look for. + + The logger, or null if the logger doesn't exist in the specified + assembly's repository. + + + + Get the currently defined loggers. + + Returns all the currently defined loggers in the default repository. + + + The root logger is not included in the returned array. + + All the defined loggers. + + + + Returns all the currently defined loggers in the specified repository. + + The repository to lookup in. + + The root logger is not included in the returned array. + + All the defined loggers. + + + + Returns all the currently defined loggers in the specified assembly's repository. + + The assembly to use to lookup the repository. + + The root logger is not included in the returned array. + + All the defined loggers. + + + Get or create a logger. + + Retrieves or creates a named logger. + + + + Retrieves a logger named as the + parameter. If the named logger already exists, then the + existing instance will be returned. Otherwise, a new instance is + created. + + By default, loggers do not have a set level but inherit + it from the hierarchy. This is one of the central features of + log4net. + + + The name of the logger to retrieve. + The logger with the name specified. + + + + Retrieves or creates a named logger. + + + + Retrieve a logger named as the + parameter. If the named logger already exists, then the + existing instance will be returned. Otherwise, a new instance is + created. + + + By default, loggers do not have a set level but inherit + it from the hierarchy. This is one of the central features of + log4net. + + + The repository to lookup in. + The name of the logger to retrieve. + The logger with the name specified. + + + + Retrieves or creates a named logger. + + + + Retrieve a logger named as the + parameter. If the named logger already exists, then the + existing instance will be returned. Otherwise, a new instance is + created. + + + By default, loggers do not have a set level but inherit + it from the hierarchy. This is one of the central features of + log4net. + + + The assembly to use to lookup the repository. + The name of the logger to retrieve. + The logger with the name specified. + + + + Shorthand for . + + + Get the logger for the fully qualified name of the type specified. + + The full name of will be used as the name of the logger to retrieve. + The logger with the name specified. + + + + Shorthand for . + + + Gets the logger for the fully qualified name of the type specified. + + The repository to lookup in. + The full name of will be used as the name of the logger to retrieve. + The logger with the name specified. + + + + Shorthand for . + + + Gets the logger for the fully qualified name of the type specified. + + The assembly to use to lookup the repository. + The full name of will be used as the name of the logger to retrieve. + The logger with the name specified. + + + + Shuts down the log4net system. + + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in all the + default repositories. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + Shutdown a logger repository. + + Shuts down the default repository. + + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in the + default repository. + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + + + + Shuts down the repository for the repository specified. + + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in the + specified. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + The repository to shutdown. + + + + Shuts down the repository specified. + + + + Calling this method will safely close and remove all + appenders in all the loggers including root contained in the + repository. The repository is looked up using + the specified. + + + Some appenders need to be closed before the application exists. + Otherwise, pending logging events might be lost. + + + The shutdown method is careful to close nested + appenders before closing regular appenders. This is allows + configurations where a regular appender is attached to a logger + and again to a nested appender. + + + The assembly to use to lookup the repository. + + + Reset the configuration of a repository + + Resets all values contained in this repository instance to their defaults. + + + + Resets all values contained in the repository instance to their + defaults. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set to its default "off" value. + + + + + + Resets all values contained in this repository instance to their defaults. + + + + Reset all values contained in the repository instance to their + defaults. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set to its default "off" value. + + + The repository to reset. + + + + Resets all values contained in this repository instance to their defaults. + + + + Reset all values contained in the repository instance to their + defaults. This removes all appenders from all loggers, sets + the level of all non-root loggers to null, + sets their additivity flag to true and sets the level + of the root logger to . Moreover, + message disabling is set to its default "off" value. + + + The assembly to use to lookup the repository to reset. + + + Get the logger repository. + + Returns the default instance. + + + + Gets the for the repository specified + by the callers assembly (). + + + The instance for the default repository. + + + + Returns the default instance. + + The default instance. + + + Gets the for the repository specified + by the argument. + + + The repository to lookup in. + + + + Returns the default instance. + + The default instance. + + + Gets the for the repository specified + by the argument. + + + The assembly to use to lookup the repository. + + + Get a logger repository. + + Returns the default instance. + + + + Gets the for the repository specified + by the callers assembly (). + + + The instance for the default repository. + + + + Returns the default instance. + + The default instance. + + + Gets the for the repository specified + by the argument. + + + The repository to lookup in. + + + + Returns the default instance. + + The default instance. + + + Gets the for the repository specified + by the argument. + + + The assembly to use to lookup the repository. + + + Create a domain + + Creates a repository with the specified repository type. + + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + The created will be associated with the repository + specified such that a call to will return + the same repository instance. + + + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + Create a logger repository. + + Creates a repository with the specified repository type. + + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + The created will be associated with the repository + specified such that a call to will return + the same repository instance. + + + + + + Creates a repository with the specified name. + + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + Creates the default type of which is a + object. + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The name of the repository, this must be unique amongst repositories. + The created for the repository. + The specified repository already exists. + + + + Creates a repository with the specified name. + + + + Creates the default type of which is a + object. + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The name of the repository, this must be unique amongst repositories. + The created for the repository. + The specified repository already exists. + + + + Creates a repository with the specified name and repository type. + + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The name of the repository, this must be unique to the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + The specified repository already exists. + + + + Creates a repository with the specified name and repository type. + + + + The name must be unique. Repositories cannot be redefined. + An will be thrown if the repository already exists. + + + The name of the repository, this must be unique to the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + The specified repository already exists. + + + + Creates a repository for the specified assembly and repository type. + + + + CreateDomain is obsolete. Use CreateRepository instead of CreateDomain. + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + The assembly to use to get the name of the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + + Creates a repository for the specified assembly and repository type. + + + + The created will be associated with the repository + specified such that a call to with the + same assembly specified will return the same repository instance. + + + The assembly to use to get the name of the repository. + A that implements + and has a no arg constructor. An instance of this type will be created to act + as the for the repository specified. + The created for the repository. + + + + Gets the list of currently defined repositories. + + + + Get an array of all the objects that have been created. + + + An array of all the known objects. + + + + Looks up the wrapper object for the logger specified. + + The logger to get the wrapper for. + The wrapper for the logger specified. + + + + Looks up the wrapper objects for the loggers specified. + + The loggers to get the wrappers for. + The wrapper objects for the loggers specified. + + + + Create the objects used by + this manager. + + The logger to wrap. + The wrapper for the logger specified. + + + + The wrapper map to use to hold the objects. + + + + + Implementation of Mapped Diagnostic Contexts. + + + + + The MDC is deprecated and has been replaced by the . + The current MDC implementation forwards to the ThreadContext.Properties. + + + + The MDC class is similar to the class except that it is + based on a map instead of a stack. It provides mapped + diagnostic contexts. A Mapped Diagnostic Context, or + MDC in short, is an instrument for distinguishing interleaved log + output from different sources. Log output is typically interleaved + when a server handles multiple clients near-simultaneously. + + + The MDC is managed on a per thread basis. + + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + Uses a private access modifier to prevent instantiation of this class. + + + + + Gets the context value identified by the parameter. + + The key to lookup in the MDC. + The string value held for the key, or a null reference if no corresponding value is found. + + + + The MDC is deprecated and has been replaced by the . + The current MDC implementation forwards to the ThreadContext.Properties. + + + + If the parameter does not look up to a + previously defined context then null will be returned. + + + + + + Add an entry to the MDC + + The key to store the value under. + The value to store. + + + + The MDC is deprecated and has been replaced by the . + The current MDC implementation forwards to the ThreadContext.Properties. + + + + Puts a context value (the parameter) as identified + with the parameter into the current thread's + context map. + + + If a value is already defined for the + specified then the value will be replaced. If the + is specified as null then the key value mapping will be removed. + + + + + + Removes the key value mapping for the key specified. + + The key to remove. + + + + The MDC is deprecated and has been replaced by the . + The current MDC implementation forwards to the ThreadContext.Properties. + + + + Remove the specified entry from this thread's MDC + + + + + + Clear all entries in the MDC + + + + + The MDC is deprecated and has been replaced by the . + The current MDC implementation forwards to the ThreadContext.Properties. + + + + Remove all the entries from this thread's MDC + + + + + + Implementation of Nested Diagnostic Contexts. + + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + A Nested Diagnostic Context, or NDC in short, is an instrument + to distinguish interleaved log output from different sources. Log + output is typically interleaved when a server handles multiple + clients near-simultaneously. + + + Interleaved log output can still be meaningful if each log entry + from different contexts had a distinctive stamp. This is where NDCs + come into play. + + + Note that NDCs are managed on a per thread basis. The NDC class + is made up of static methods that operate on the context of the + calling thread. + + + How to push a message into the context + + using(NDC.Push("my context message")) + { + ... all log calls will have 'my context message' included ... + + } // at the end of the using block the message is automatically removed + + + + Nicko Cadell + Gert Driesen + + + + Initializes a new instance of the class. + + + Uses a private access modifier to prevent instantiation of this class. + + + + + Clears all the contextual information held on the current thread. + + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + Clears the stack of NDC data held on the current thread. + + + + + + Creates a clone of the stack of context information. + + A clone of the context info for this thread. + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + The results of this method can be passed to the + method to allow child threads to inherit the context of their + parent thread. + + + + + + Inherits the contextual information from another thread. + + The context stack to inherit. + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + This thread will use the context information from the stack + supplied. This can be used to initialize child threads with + the same contextual information as their parent threads. These + contexts will NOT be shared. Any further contexts that + are pushed onto the stack will not be visible to the other. + Call to obtain a stack to pass to + this method. + + + + + + Removes the top context from the stack. + + + The message in the context that was removed from the top + of the stack. + + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + Remove the top context from the stack, and return + it to the caller. If the stack is empty then an + empty string (not null) is returned. + + + + + + Pushes a new context message. + + The new context message. + + An that can be used to clean up + the context stack. + + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + Pushes a new context onto the context stack. An + is returned that can be used to clean up the context stack. This + can be easily combined with the using keyword to scope the + context. + + + Simple example of using the Push method with the using keyword. + + using(log4net.NDC.Push("NDC_Message")) + { + log.Warn("This should have an NDC message"); + } + + + + + + Removes the context information for this thread. It is + not required to call this method. + + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + This method is not implemented. + + + + + + Forces the stack depth to be at most . + + The maximum depth of the stack + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + Forces the stack depth to be at most . + This may truncate the head of the stack. This only affects the + stack in the current thread. Also it does not prevent it from + growing, it only sets the maximum depth at the time of the + call. This can be used to return to a known context depth. + + + + + + Gets the current context depth. + + The current context depth. + + + + The NDC is deprecated and has been replaced by the . + The current NDC implementation forwards to the ThreadContext.Stacks["NDC"]. + + + + The number of context values pushed onto the context stack. + + + Used to record the current depth of the context. This can then + be restored using the method. + + + + + + + The log4net Thread Context. + + + + The ThreadContext provides a location for thread specific debugging + information to be stored. + The ThreadContext properties override any + properties with the same name. + + + The thread context has a properties map and a stack. + The properties and stack can + be included in the output of log messages. The + supports selecting and outputting these properties. + + + The Thread Context provides a diagnostic context for the current thread. + This is an instrument for distinguishing interleaved log + output from different sources. Log output is typically interleaved + when a server handles multiple clients near-simultaneously. + + + The Thread Context is managed on a per thread basis. + + + Example of using the thread context properties to store a username. + + ThreadContext.Properties["user"] = userName; + log.Info("This log message has a ThreadContext Property called 'user'"); + + + Example of how to push a message into the context stack + + using(ThreadContext.Stacks["NDC"].Push("my context message")) + { + log.Info("This log message has a ThreadContext Stack message that includes 'my context message'"); + + } // at the end of the using block the message is automatically popped + + + + Nicko Cadell + + + + Private Constructor. + + + + Uses a private access modifier to prevent instantiation of this class. + + + + + + The thread context properties instance + + + + + The thread context stacks instance + + + + + The thread properties map + + + The thread properties map + + + + The ThreadContext properties override any + properties with the same name. + + + + + + The thread stacks + + + stack map + + + + The thread local stacks. + + + + + diff --git a/src/DotNetMQ.sln b/src/DotNetMQ.sln new file mode 100644 index 0000000..8982d3a --- /dev/null +++ b/src/DotNetMQ.sln @@ -0,0 +1,94 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetMQ", "DotNetMQ\DotNetMQ.csproj", "{7634394B-5A25-4DCC-849A-D7A8FDEFF9FC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MDSCommonLib", "MDSCommonLib\MDSCommonLib.csproj", "{055149D1-A267-4E2E-B8AE-EA7848A45701}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MDSCore", "MDSCore\MDSCore.csproj", "{AB59B8B2-D414-483F-AA72-A2644D92C86D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MDSManager", "MDSManager\MDSManager.csproj", "{FD323317-5B22-413C-8ADC-6701C2B7EB76}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{A5C714B5-FAB7-4934-9761-71E0CE785B8F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MDSServiceProxyGenerator", "Tools\MDSServiceProxyGenerator\MDSServiceProxyGenerator.csproj", "{B49D18FF-DD77-46A4-880A-F0B9598C6070}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dependencies", "Dependencies", "{BC8AA727-4F74-4186-9018-95F4BA963DD0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Databases", "Databases", "{49BF0DB3-6E82-414C-BAAE-03094AF323F3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{05F27B70-CF54-4DFF-B6EB-C86D4352ACE1}" + ProjectSection(SolutionItems) = preProject + Dependencies\Libraries\log4net.dll = Dependencies\Libraries\log4net.dll + Dependencies\Libraries\log4net.xml = Dependencies\Libraries\log4net.xml + Dependencies\Libraries\MySql.Data.dll = Dependencies\Libraries\MySql.Data.dll + Dependencies\Libraries\System.Data.SQLite.dll = Dependencies\Libraries\System.Data.SQLite.dll + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MySQL", "MySQL", "{D94CC304-A7EC-43EC-BE0F-E0EEFE8683F0}" + ProjectSection(SolutionItems) = preProject + Dependencies\Databases\MySQL\ReadMe.txt = Dependencies\Databases\MySQL\ReadMe.txt + Dependencies\Databases\MySQL\Tables.txt = Dependencies\Databases\MySQL\Tables.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SQLite", "SQLite", "{764E147F-65EF-486E-B470-BD74E93048A0}" + ProjectSection(SolutionItems) = preProject + Dependencies\Databases\SQLite\MDS.s3db = Dependencies\Databases\SQLite\MDS.s3db + Dependencies\Databases\SQLite\ReadMe.txt = Dependencies\Databases\SQLite\ReadMe.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9F4A05C3-ADFD-411D-9151-FBA5463C8441}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerTest", "Tests\ServerTest\ServerTest.csproj", "{4B4B3551-78B8-4CE2-BD0A-98E8EC496E80}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MSSQL", "MSSQL", "{DFDDFE51-1B60-443B-888D-B4ACB3B0D0DB}" + ProjectSection(SolutionItems) = preProject + Dependencies\Databases\MSSQL\DB-FILES.zip = Dependencies\Databases\MSSQL\DB-FILES.zip + Dependencies\Databases\MSSQL\ReadMe.txt = Dependencies\Databases\MSSQL\ReadMe.txt + Dependencies\Databases\MSSQL\Tables.txt = Dependencies\Databases\MSSQL\Tables.txt + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7634394B-5A25-4DCC-849A-D7A8FDEFF9FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7634394B-5A25-4DCC-849A-D7A8FDEFF9FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7634394B-5A25-4DCC-849A-D7A8FDEFF9FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7634394B-5A25-4DCC-849A-D7A8FDEFF9FC}.Release|Any CPU.Build.0 = Release|Any CPU + {055149D1-A267-4E2E-B8AE-EA7848A45701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {055149D1-A267-4E2E-B8AE-EA7848A45701}.Debug|Any CPU.Build.0 = Debug|Any CPU + {055149D1-A267-4E2E-B8AE-EA7848A45701}.Release|Any CPU.ActiveCfg = Release|Any CPU + {055149D1-A267-4E2E-B8AE-EA7848A45701}.Release|Any CPU.Build.0 = Release|Any CPU + {AB59B8B2-D414-483F-AA72-A2644D92C86D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB59B8B2-D414-483F-AA72-A2644D92C86D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB59B8B2-D414-483F-AA72-A2644D92C86D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB59B8B2-D414-483F-AA72-A2644D92C86D}.Release|Any CPU.Build.0 = Release|Any CPU + {FD323317-5B22-413C-8ADC-6701C2B7EB76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD323317-5B22-413C-8ADC-6701C2B7EB76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD323317-5B22-413C-8ADC-6701C2B7EB76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD323317-5B22-413C-8ADC-6701C2B7EB76}.Release|Any CPU.Build.0 = Release|Any CPU + {B49D18FF-DD77-46A4-880A-F0B9598C6070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B49D18FF-DD77-46A4-880A-F0B9598C6070}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B49D18FF-DD77-46A4-880A-F0B9598C6070}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B49D18FF-DD77-46A4-880A-F0B9598C6070}.Release|Any CPU.Build.0 = Release|Any CPU + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B49D18FF-DD77-46A4-880A-F0B9598C6070} = {A5C714B5-FAB7-4934-9761-71E0CE785B8F} + {49BF0DB3-6E82-414C-BAAE-03094AF323F3} = {BC8AA727-4F74-4186-9018-95F4BA963DD0} + {05F27B70-CF54-4DFF-B6EB-C86D4352ACE1} = {BC8AA727-4F74-4186-9018-95F4BA963DD0} + {D94CC304-A7EC-43EC-BE0F-E0EEFE8683F0} = {49BF0DB3-6E82-414C-BAAE-03094AF323F3} + {764E147F-65EF-486E-B470-BD74E93048A0} = {49BF0DB3-6E82-414C-BAAE-03094AF323F3} + {DFDDFE51-1B60-443B-888D-B4ACB3B0D0DB} = {49BF0DB3-6E82-414C-BAAE-03094AF323F3} + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80} = {9F4A05C3-ADFD-411D-9151-FBA5463C8441} + EndGlobalSection +EndGlobal diff --git a/src/DotNetMQ/App.config b/src/DotNetMQ/App.config new file mode 100644 index 0000000..c5fd44f --- /dev/null +++ b/src/DotNetMQ/App.config @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DotNetMQ/DotNetMQ.csproj b/src/DotNetMQ/DotNetMQ.csproj new file mode 100644 index 0000000..56f6667 --- /dev/null +++ b/src/DotNetMQ/DotNetMQ.csproj @@ -0,0 +1,106 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7634394B-5A25-4DCC-849A-D7A8FDEFF9FC} + WinExe + Properties + DotNetMQ + DotNetMQ + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\Libraries\log4net.dll + + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + Component + + + DotNetMqService.cs + + + + Component + + + ProjectInstaller.cs + + + + + + {055149D1-A267-4E2E-B8AE-EA7848A45701} + MDSCommonLib + + + {AB59B8B2-D414-483F-AA72-A2644D92C86D} + MDSCore + + + + + ProjectInstaller.cs + + + + + + Always + + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/src/DotNetMQ/DotNetMqService.Designer.cs b/src/DotNetMQ/DotNetMqService.Designer.cs new file mode 100644 index 0000000..4231eb8 --- /dev/null +++ b/src/DotNetMQ/DotNetMqService.Designer.cs @@ -0,0 +1,37 @@ +namespace DotNetMQ +{ + partial class DotNetMqService + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "DotNetMqService"; + } + + #endregion + } +} diff --git a/src/DotNetMQ/DotNetMqService.cs b/src/DotNetMQ/DotNetMqService.cs new file mode 100644 index 0000000..1dde631 --- /dev/null +++ b/src/DotNetMQ/DotNetMqService.cs @@ -0,0 +1,77 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using System.ServiceProcess; +using log4net; +using MDS; + +namespace DotNetMQ +{ + /// + /// This service is used to run DotNetMQ as a Windows Service. + /// + public partial class DotNetMqService : ServiceBase + { + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to MDS server instance. + /// + private readonly MDSServer _mdsServer; + + /// + /// Constructor. + /// + public DotNetMqService() + { + InitializeComponent(); + _mdsServer = new MDSServer(); + } + + protected override void OnStart(string[] args) + { + try + { + _mdsServer.Start(); + } + catch (Exception ex) + { + Logger.Fatal(ex.Message, ex); + Stop(); + } + } + + protected override void OnStop() + { + try + { + _mdsServer.Stop(true); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + } +} diff --git a/src/DotNetMQ/Liscense.txt b/src/DotNetMQ/Liscense.txt new file mode 100644 index 0000000..83a488d --- /dev/null +++ b/src/DotNetMQ/Liscense.txt @@ -0,0 +1,16 @@ +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/DotNetMQ/MDSSettings.design.xml b/src/DotNetMQ/MDSSettings.design.xml new file mode 100644 index 0000000..0f978c4 --- /dev/null +++ b/src/DotNetMQ/MDSSettings.design.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/DotNetMQ/MDSSettings.xml b/src/DotNetMQ/MDSSettings.xml new file mode 100644 index 0000000..9b73fda --- /dev/null +++ b/src/DotNetMQ/MDSSettings.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DotNetMQ/Program.cs b/src/DotNetMQ/Program.cs new file mode 100644 index 0000000..0ba383d --- /dev/null +++ b/src/DotNetMQ/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; + +namespace DotNetMQ +{ + static class Program + { + /// + /// The main entry point for the application. + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new DotNetMqService() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/src/DotNetMQ/ProjectInstaller.Designer.cs b/src/DotNetMQ/ProjectInstaller.Designer.cs new file mode 100644 index 0000000..4e863d5 --- /dev/null +++ b/src/DotNetMQ/ProjectInstaller.Designer.cs @@ -0,0 +1,60 @@ +namespace DotNetMQ +{ + partial class ProjectInstaller + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.DotNetMqProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller(); + this.DotNetMqServiceInstaller = new System.ServiceProcess.ServiceInstaller(); + // + // DotNetMqProcessInstaller + // + this.DotNetMqProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem; + this.DotNetMqProcessInstaller.Password = null; + this.DotNetMqProcessInstaller.Username = null; + // + // DotNetMqServiceInstaller + // + this.DotNetMqServiceInstaller.Description = "A Message Broker for integration of applications."; + this.DotNetMqServiceInstaller.DisplayName = "DotNetMQ"; + this.DotNetMqServiceInstaller.ServiceName = "DotNetMqService"; + this.DotNetMqServiceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic; + // + // ProjectInstaller + // + this.Installers.AddRange(new System.Configuration.Install.Installer[] { + this.DotNetMqProcessInstaller, + this.DotNetMqServiceInstaller}); + + } + + #endregion + + private System.ServiceProcess.ServiceProcessInstaller DotNetMqProcessInstaller; + private System.ServiceProcess.ServiceInstaller DotNetMqServiceInstaller; + } +} \ No newline at end of file diff --git a/src/DotNetMQ/ProjectInstaller.cs b/src/DotNetMQ/ProjectInstaller.cs new file mode 100644 index 0000000..e12b8f5 --- /dev/null +++ b/src/DotNetMQ/ProjectInstaller.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.Linq; + + +namespace DotNetMQ +{ + [RunInstaller(true)] + public partial class ProjectInstaller : Installer + { + public ProjectInstaller() + { + InitializeComponent(); + } + } +} diff --git a/src/DotNetMQ/ProjectInstaller.resx b/src/DotNetMQ/ProjectInstaller.resx new file mode 100644 index 0000000..1cc5b04 --- /dev/null +++ b/src/DotNetMQ/ProjectInstaller.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 196, 17 + + + False + + \ No newline at end of file diff --git a/src/DotNetMQ/Properties/AssemblyInfo.cs b/src/DotNetMQ/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5e45172 --- /dev/null +++ b/src/DotNetMQ/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("DotNetMQ")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("DotNetMQ")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: log4net.Config.XmlConfigurator(Watch = true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0a079b68-2bd1-4b5f-949c-a3bbe6a8e53c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.1.0")] +[assembly: AssemblyFileVersion("0.9.1.0")] diff --git a/src/DotNetMQ/SqliteDB/MDS.s3db b/src/DotNetMQ/SqliteDB/MDS.s3db new file mode 100644 index 0000000..1a1cdce Binary files /dev/null and b/src/DotNetMQ/SqliteDB/MDS.s3db differ diff --git a/src/MDSCommonLib/Changes.txt b/src/MDSCommonLib/Changes.txt new file mode 100644 index 0000000..0474ec2 --- /dev/null +++ b/src/MDSCommonLib/Changes.txt @@ -0,0 +1,2 @@ +# Version 0.9.0.0 - 09.05.2011 - Halil İbrahim KALKAN + First release. \ No newline at end of file diff --git a/src/MDSCommonLib/Client/AppService/IMDSApplication.cs b/src/MDSCommonLib/Client/AppService/IMDSApplication.cs new file mode 100644 index 0000000..022f8c0 --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/IMDSApplication.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.AppService +{ + /// + /// Represents a MDS Application from MDSMessageProcessor perspective. + /// This class also provides a static collection that can be used as a cache, + /// thus, an MDSMessageProcessor/MDSClientApplicationBase can store/get application-wide objects. + /// + public interface IMDSApplication + { + /// + /// Name of the application. + /// + string ApplicationName { get; } + + /// + /// Gets/Sets application-wide object by a string key. + /// + /// Key of the object to access it + /// Object, that is related with given key + object this[string key] { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/AppService/IMDSServer.cs b/src/MDSCommonLib/Client/AppService/IMDSServer.cs new file mode 100644 index 0000000..be14ca9 --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/IMDSServer.cs @@ -0,0 +1,34 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.AppService +{ + /// + /// This interface is used by MDSMessageProcessor/MDSClientApplicationBase to perform operations on MDSServer, + /// for example; creating messages to send. + /// + public interface IMDSServer + { + /// + /// Creates an empty message to send. + /// + /// Created message + IOutgoingMessage CreateMessage(); + } +} diff --git a/src/MDSCommonLib/Client/AppService/MDSAppServiceBase.cs b/src/MDSCommonLib/Client/AppService/MDSAppServiceBase.cs new file mode 100644 index 0000000..1ea22a6 --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/MDSAppServiceBase.cs @@ -0,0 +1,37 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.AppService +{ + /// + /// Base class for MDSMessageProcessor/MDSClientApplicationBase. + /// + public abstract class MDSAppServiceBase + { + /// + /// Reference to the MDS Server. + /// + public IMDSServer Server { get; set; } + + /// + /// Reference to this Application. + /// + public IMDSApplication Application { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/AppService/MDSClientApplicationBase.cs b/src/MDSCommonLib/Client/AppService/MDSClientApplicationBase.cs new file mode 100644 index 0000000..1bddc0e --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/MDSClientApplicationBase.cs @@ -0,0 +1,43 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.AppService +{ + /// + /// Plug-In applications may derive this class to perform some operations on starting/stopping of MDS. + /// + public abstract class MDSClientApplicationBase : MDSAppServiceBase + { + /// + /// This method is called on startup of MDS. + /// + public virtual void OnStart() + { + //No action + } + + /// + /// This method is called on stopping of MDS. + /// + public virtual void OnStop() + { + //No action + } + } +} diff --git a/src/MDSCommonLib/Client/AppService/MDSMessageProcessor.cs b/src/MDSCommonLib/Client/AppService/MDSMessageProcessor.cs new file mode 100644 index 0000000..173a311 --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/MDSMessageProcessor.cs @@ -0,0 +1,49 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.AppService +{ + /// + /// This class is used as base class for classes that are processing messages of an application concurrently. Thus, + /// an application can process more than one message in a time. + /// MDS creates an instance of this class for every incoming message to process it. + /// Maximum limit of messages that are being processed at the same time is configurable for individual applications. + /// + public abstract class MDSMessageProcessor : MDSAppServiceBase + { + /// + /// Used to get/set if messages are auto acknowledged. + /// If AutoAcknowledgeMessages is true, then messages are automatically acknowledged after MessageReceived event, + /// if they are not acknowledged/rejected before by application. + /// Default: true. + /// + protected bool AutoAcknowledgeMessages + { + get { return _autoAcknowledgeMessages; } + set { _autoAcknowledgeMessages = value; } + } + private bool _autoAcknowledgeMessages = true; + + /// + /// This method is called by MDS server to process the message, when a message is sent to this application. + /// + /// Message to process + public abstract void ProcessMessage(IIncomingMessage message); + } +} diff --git a/src/MDSCommonLib/Client/AppService/README.txt b/src/MDSCommonLib/Client/AppService/README.txt new file mode 100644 index 0000000..ea49f77 --- /dev/null +++ b/src/MDSCommonLib/Client/AppService/README.txt @@ -0,0 +1 @@ +This folder/namespace is not used yet. It is planned for future... \ No newline at end of file diff --git a/src/MDSCommonLib/Client/IIncomingMessage.cs b/src/MDSCommonLib/Client/IIncomingMessage.cs new file mode 100644 index 0000000..da78f90 --- /dev/null +++ b/src/MDSCommonLib/Client/IIncomingMessage.cs @@ -0,0 +1,145 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Client +{ + /// + /// Represents an incoming data message for a MDS client. + /// + public interface IIncomingMessage + { + #region Properties + + /// + /// Acknowledgment state of the message. + /// + MessageAckStates AckState { get; } + + /// + /// Gets the unique identifier for this message. + /// + string MessageId { get; } + + /// + /// If this message is a reply for another message then RepliedMessageId contains first message's MessageId + /// else RepliedMessageId is null as default. + /// + string RepliedMessageId { get; } + + /// + /// Name of the first source server of the message. + /// + string SourceServerName { get; } + + /// + /// Name of the sender application of the message. + /// + string SourceApplicationName { get; } + + /// + /// The source communication channel's (Communicator's) unique Id. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicate a spesific communicator. + /// This field is set by MDS automatically. + /// + long SourceCommunicatorId { get; } + + /// + /// Name of the final destination server of the message. + /// + string DestinationServerName { get; } + + /// + /// Name of the final destination application of the message. + /// + string DestinationApplicationName { get; } + + /// + /// Destination communication channel's (Communicator's) Id. + /// This field is used by MDS to deliver message to a spesific communicator. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicate a spesific communicator as receiver of message. + /// If it is set to 0 (zero), message may be delivered to any connected communicator. + /// + long DestinationCommunicatorId { get; } + + /// + /// Passed servers of message until now, includes source and destination servers. + /// + ServerTransmitReport[] PassedServers { get; } + + /// + /// Essential application message data that is received. + /// + byte[] MessageData { get; } + + /// + /// Transmit rule of message. + /// This is important because it determines persistence and transmit time of message. + /// Default: StoreAndForward. + /// + MessageTransmitRules TransmitRule { get; } + + #endregion + + #region Methods + + /// + /// Used to Acknowledge this message. + /// Confirms that the message is received safely by client application. + /// A message must be acknowledged by client application to remove message from message queue. + /// MDS server doesn't send next message to the client application until the message is acknowledged. + /// Also, MDS server sends same message again if the message is not acknowledged in a certain time. + /// + void Acknowledge(); + + /// + /// Used to reject (to not acknowledge) this message. + /// Indicates that the message can not received correctly or can not handled the message, and the message + /// must be sent to client application later again. + /// If MDS server gets reject for a message, + /// it doesn't send any message to the client application instance for a while. + /// If message is persistent, than it is sent to another instance of application or to same application instance again. + /// If message is not persistent, it is deleted. + /// + void Reject(); + + /// + /// Used to reject (to not acknowledge) this message. + /// Indicates that the message can not received correctly or can not handled the message, and the message + /// must be sent to client application later again. + /// If MDS server gets reject for a message, + /// it doesn't send any message to the client application instance for a while. + /// If message is persistent, than it is sent to another instance of application or to same application instance again. + /// If message is not persistent, it is deleted. + /// + /// Reject reason + void Reject(string reason); + + /// + /// Creates a empty response message for this message. + /// + /// Response message object + IOutgoingMessage CreateResponseMessage(); + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/IOutgoingMessage.cs b/src/MDSCommonLib/Client/IOutgoingMessage.cs new file mode 100644 index 0000000..5960e1a --- /dev/null +++ b/src/MDSCommonLib/Client/IOutgoingMessage.cs @@ -0,0 +1,133 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Client +{ + /// + /// Represents an outgoing data message for a MDS client. + /// + public interface IOutgoingMessage + { + #region Properties + + /// + /// Unique ID for this message. + /// This will be a GUID if it is not set. + /// It is recommended to leave this field as default. + /// + string MessageId { get; set; } + + /// + /// If this message is a reply for another message then RepliedMessageId contains first message's MessageId + /// else RepliedMessageId is null default. + /// + string RepliedMessageId { get; set; } + + /// + /// Name of the first source server of the message. + /// If this field is leaved null/empty, it is set by MDS server automatically. + /// + string SourceServerName { get; set; } + + /// + /// Name of the first sender application of the message. + /// If this field is leaved null/empty, it is set by MDS server as sender application's name automatically. + /// + string SourceApplicationName { get; set; } + + /// + /// Name of the final destination server of the message. + /// If this field is leaved null/empty, it is set by MDS server as + /// sender application's server's name automatically. + /// It may be leaved null to send a message to an application on the same server. + /// + string DestinationServerName { get; set; } + + /// + /// Name of the final destination application of the message. + /// If this field is leaved null/empty, it is set by MDS server as sender application's name automatically. + /// It may be leaved null to send a message to same application. + /// + string DestinationApplicationName { get; set; } + + /// + /// Destination communication channel's (Communicator's) Id. + /// This field is used by MDS to deliver message to a spesific communicator. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicates a spesific communicator as receiver of message. + /// If it is set to 0 (zero), message may be delivered to any connected communicator. + /// If there is no communicator with DestinationCommunicatorId, message can not be delivered, so, + /// this field can only be used to send non-persistent messages (Syncronous messages). + /// + long DestinationCommunicatorId { get; set; } + + /// + /// Essential application message data to be sent. + /// + byte[] MessageData { get; set; } + + /// + /// Transmit rule of message. + /// This is important because it determines persistence and transmit time of message. + /// Default: StoreAndForward. + /// + MessageTransmitRules TransmitRule { get; set; } + + #endregion + + #region Methods + + /// + /// Sends the message to the MDS server. + /// If this method does not throw an exception, + /// message is correctly delivered to MDS server (persistent message) + /// or to the destination application (non persistent message). + /// + void Send(); + + /// + /// Sends the message to the MDS server. + /// If this method does not throw an exception, + /// message is correctly delivered to MDS server (persistent message) + /// or to the destination application (non persistent message). + /// + /// Timeout to send message as milliseconds + void Send(int timeoutMilliseconds); + + /// + /// Sends the message and waits for an incoming message for that message. + /// MDS can be used for Request/Response type messaging with this method. + /// Default timeout value: 5 minutes. + /// + /// Response message + IIncomingMessage SendAndGetResponse(); + + /// + /// Sends the message and waits for an incoming message for that message. + /// MDS can be used for Request/Response type messaging with this method. + /// + /// Timeout to get response message + /// Response message + IIncomingMessage SendAndGetResponse(int timeoutMilliseconds); + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/MDSClient.cs b/src/MDSCommonLib/Client/MDSClient.cs new file mode 100644 index 0000000..5f9f42b --- /dev/null +++ b/src/MDSCommonLib/Client/MDSClient.cs @@ -0,0 +1,943 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication; +using MDS.Communication.Channels; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Threading; + +namespace MDS.Client +{ + /// + /// This is the main class that is used by an client application to communicate with MDS server. + /// + public class MDSClient : IDisposable + { + #region Events + + /// + /// This event is raised when a data transfer message received from MDS server. + /// + public event MessageReceivedHandler MessageReceived; + + #endregion + + #region Public properties + + /// + /// Name of the client application. + /// + public string ApplicationName { get; private set; } + + /// + /// Gets/Sets Communication Way between MDS server. + /// A receiver may want to do not receive messages anymore, by changing it's communication way to 'Send'. + /// Setting this property sends a MDSChangeCommunicationWayMessage message to MDS server. + /// Default value: CommunicationWays.SendAndReceive + /// + public CommunicationWays CommunicationWay + { + get { return _communicationChannel.CommunicationWay; } + set { ChangeCommunicationWay(value); } + } + + /// + /// Communicator Id of this instance of application in MDS. + /// This field is valid only if client application is connected and registered to the MDS server. + /// + public long CommunicatorId + { + get { return _communicationChannel.ComminicatorId; } + } + + /// + /// Gets/sets Reconnecting option on any error case. + /// If this is true, client application attempts to reconnect to MDS server until it is connected and + /// doesn't throw exceptions while connecting. + /// Default value: True. + /// + public bool ReConnectServerOnError { get; set; } + + /// + /// Used to get/set if messages are auto acknowledged. + /// If AutoAcknowledgeMessages is true, then messages are automatically acknowledged after MessageReceived event, + /// if they are not acknowledged/rejected before by application. + /// Default: false. + /// + public bool AutoAcknowledgeMessages { get; set; } + + /// + /// Timeout value for outgoing data messages. + /// Default value: 300000 (5 minutes). + /// + public int DataMessageTimeout { get; set; } + + /// + /// Time of last message received from MDS server. + /// + public DateTime LastIncomingMessageTime { get; private set; } + + /// + /// Time of last message sent to MDS server. + /// + public DateTime LastOutgoingMessageTime { get; private set; } + + /// + /// MessageId of last received and acknowledged message's Id. + /// This field is used to do not receive/accept same message again. + /// If a message is send by MDS server with same MessageId, + /// message is discarded and ACK message is sent to server. + /// + public string LastAcknowledgedMessageId { get; private set; } + + #endregion + + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Communication channel that is used to communicate with MDS server. + /// + private readonly ICommunicationChannel _communicationChannel; + + /// + /// This messages are waiting for a response. + /// Key: MessageID of waiting request message. + /// Value: A WaitingMessage instance. + /// + private readonly SortedList _waitingMessages; + + /// + /// This queue is used to queue and process sequentially messages that are received from MDS server. + /// + private readonly QueueProcessorThread _incomingMessageQueue; + + /// + /// This timer is used to reconnect to MDS server if it is disconnected. + /// + private readonly Timer _reconnectTimer; + + /// + /// Used to Start/Stop MDSClient, and indicates the state. + /// + private volatile bool _running; + + #endregion + + #region Constructors + + /// + /// Creates a new MDSClient object using default IP address (127.0.0.1) and default TCP port. + /// + /// Name of the client application that connects to MDS server + public MDSClient(string applicationName) + : this(applicationName, CommunicationConsts.DefaultIpAddress, CommunicationConsts.DefaultMDSPort) + { + + } + + /// + /// Creates a new MDSClient object using default TCP port. + /// + /// Name of the client application that connects to MDS server + /// Ip address of the MDS server + public MDSClient(string applicationName, string ipAddress) + : this(applicationName, ipAddress, CommunicationConsts.DefaultMDSPort) + { + + } + + /// + /// Creates a new MDSClient object. + /// + /// Name of the client application that connects to MDS server + /// Ip address of the MDS server + /// Listening TCP Port of MDS server + public MDSClient(string applicationName, string ipAddress, int port) + { + DataMessageTimeout = 300000; + + ApplicationName = applicationName; + ReConnectServerOnError = true; + + _waitingMessages = new SortedList(); + _reconnectTimer = new Timer(ReconnectTimer_Tick, null, Timeout.Infinite, Timeout.Infinite); + + _incomingMessageQueue = new QueueProcessorThread(); + _incomingMessageQueue.ProcessItem += IncomingMessageQueue_ProcessItem; + + _communicationChannel = new TCPChannel(ipAddress, port); + _communicationChannel.StateChanged += CommunicationChannel_StateChanged; + _communicationChannel.MessageReceived += CommunicationChannel_MessageReceived; + + LastIncomingMessageTime = DateTime.MinValue; + LastOutgoingMessageTime = DateTime.MinValue; + } + + #endregion + + #region Public methods + + /// + /// Connects to MDS server. + /// If ReConnectServerOnError is true, than does not throw any Exception. + /// Else, throws Exception if can not connect to the Server. + /// + public void Connect() + { + _incomingMessageQueue.Start(); + + try + { + _running = true; + ConnectAndRegister(); + } + catch (Exception) + { + if (!ReConnectServerOnError) + { + _running = false; + throw; + } + } + + _reconnectTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + + /// + /// Disconnects from MDS server. + /// + public void Disconnect() + { + lock (_reconnectTimer) + { + _running = false; + _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + CloseCommunicationChannel(); + _incomingMessageQueue.Stop(true); + } + + /// + /// Creates an empty message to send. + /// + /// Created message + public IOutgoingMessage CreateMessage() + { + return new OutgoingDataMessage(this); + } + + /// + /// Disposes this object. + /// + public void Dispose() + { + if (_communicationChannel != null) + { + Disconnect(); + } + } + + #endregion + + #region Private methods + + /// + /// Connects and registers to MDS server. + /// + private void ConnectAndRegister() + { + _communicationChannel.Connect(); + try + { + var registerMessage = new MDSRegisterMessage + { + CommunicationWay = _communicationChannel.CommunicationWay, + CommunicatorType = CommunicatorTypes.Application, + Name = ApplicationName, + Password = "" + }; + var reply = SendAndWaitForReply( + registerMessage, + MDSMessageFactory.MessageTypeIdMDSOperationResultMessage, + 30000) as MDSOperationResultMessage; + + if (reply == null) + { + throw new MDSException("Can not send register message to the server."); + } + + if (!reply.Success) + { + CloseCommunicationChannel(); + throw new MDSException("Can not register to server. Detail: " + (reply.ResultText ?? "")); + } + + //reply.ResultText must be CommunicatorId if successfully registered. + _communicationChannel.ComminicatorId = Convert.ToInt64(reply.ResultText); + } + catch (MDSTimeoutException) + { + CloseCommunicationChannel(); + throw new MDSTimeoutException("Timeout occured. Can not registered to MDS server."); + } + } + + /// + /// Changes communication way of this application. + /// So, a receiver may want to do not receive messages anymore by changing it's communication way to 'Send'. + /// + /// New communication way + private void ChangeCommunicationWay(CommunicationWays newCommunicationWay) + { + if (_communicationChannel == null || + _communicationChannel.State != CommunicationStates.Connected || + _communicationChannel.CommunicationWay == newCommunicationWay) + { + return; + } + + _communicationChannel.SendMessage( + new MDSChangeCommunicationWayMessage + { + NewCommunicationWay = newCommunicationWay + }); + _communicationChannel.CommunicationWay = newCommunicationWay; + } + + /// + /// Sends a mssage to MDS server and waits a response for timeoutMilliseconds milliseconds. + /// + /// Message to send + /// What type of response is being waited + /// Maximum waiting time for response + /// Received message from server + private MDSMessage SendAndWaitForReply(MDSMessage message, int waitingResponseType, int timeoutMilliseconds) + { + //Create a WaitingMessage to wait and get response message and add it to waiting messages + var waitingMessage = new WaitingMessage(waitingResponseType); + lock (_waitingMessages) + { + _waitingMessages[message.MessageId] = waitingMessage; + } + + try + { + //Create a WaitingMessage to wait and get response message and add it to waiting messages + SendMessageInternal(message); + + //Send message to the server + waitingMessage.WaitEvent.WaitOne(timeoutMilliseconds); + + //Check for errors + switch (waitingMessage.State) + { + case WaitingMessageStates.WaitingForResponse: + throw new MDSTimeoutException("Timeout occured. Can not received response to message."); + case WaitingMessageStates.ClientDisconnected: + throw new MDSException("Client disconnected from MDS server before response received to message."); + case WaitingMessageStates.MessageRejected: + throw new MDSException("Message is rejected by MDS server or destination application."); + } + + if (waitingMessage.ResponseMessage == null) + { + throw new MDSException("An unexpected error occured, message can not send."); + } + + return waitingMessage.ResponseMessage; + } + finally + { + //Remove message from waiting messages + lock (_waitingMessages) + { + if (_waitingMessages.ContainsKey(message.MessageId)) + { + _waitingMessages.Remove(message.MessageId); + } + } + } + } + + /// + /// Sends a MDSMessage object to MDS server. + /// + /// + private void SendMessageInternal(MDSMessage message) + { + try + { + _communicationChannel.SendMessage(message); + LastOutgoingMessageTime = DateTime.Now; + } + catch (Exception) + { + CloseCommunicationChannel(); + throw; + } + } + + /// + /// This method handles StateChanged event of communication channel. + /// + /// The communication channel + /// Event arguments + private void CommunicationChannel_StateChanged(ICommunicationChannel sender, CommunicationStateChangedEventArgs e) + { + //Process only Closed event + if (sender.State != CommunicationStates.Closed) + { + return; + } + + //Pulse waiting threads for incoming messages, because disconnected to the server, and can not receive message anymore. + lock (_waitingMessages) + { + foreach (var waitingMessage in _waitingMessages.Values) + { + waitingMessage.State = WaitingMessageStates.ClientDisconnected; + waitingMessage.WaitEvent.Set(); + } + + _waitingMessages.Clear(); + } + } + + /// + /// This event handles incoming messages from communication channel. + /// + /// Communication channel that received message + /// Event arguments + private void CommunicationChannel_MessageReceived(ICommunicationChannel sender, Communication.Channels.MessageReceivedEventArgs e) + { + //Update last incoming message time + LastIncomingMessageTime = DateTime.Now; + + //Check for duplicate messages. + if (e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSDataTransferMessage) + { + var dataMessage = e.Message as MDSDataTransferMessage; + if (dataMessage != null) + { + if (dataMessage.MessageId == LastAcknowledgedMessageId) + { + try + { + SendMessageInternal(new MDSOperationResultMessage + { + RepliedMessageId = dataMessage.MessageId, + Success = true, + ResultText = "Duplicate message." + }); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + + return; + } + } + } + + //Check if there is a waiting thread for this message (in SendAndWaitForReply method) + if (!string.IsNullOrEmpty(e.Message.RepliedMessageId)) + { + WaitingMessage waitingMessage = null; + lock (_waitingMessages) + { + if (_waitingMessages.ContainsKey(e.Message.RepliedMessageId)) + { + waitingMessage = _waitingMessages[e.Message.RepliedMessageId]; + } + } + + if (waitingMessage != null) + { + if (waitingMessage.WaitingResponseType == e.Message.MessageTypeId) + { + waitingMessage.ResponseMessage = e.Message; + waitingMessage.State = WaitingMessageStates.ResponseReceived; + waitingMessage.WaitEvent.Set(); + return; + } + + if(e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSOperationResultMessage) + { + var resultMessage = e.Message as MDSOperationResultMessage; + if ((resultMessage != null) && (!resultMessage.Success)) + { + waitingMessage.State = WaitingMessageStates.MessageRejected; + waitingMessage.WaitEvent.Set(); + return; + } + } + } + } + + //If this message is not a response, then add it to message process queue + _incomingMessageQueue.Add(e.Message); + } + + /// + /// This event handles processing messages when a message is added to queue (_incomingMessageQueue). + /// + /// Reference to message queue + /// Event arguments + private void IncomingMessageQueue_ProcessItem(object sender, ProcessQueueItemEventArgs e) + { + //Process only MDSDataTransferMessage objects. + if (e.ProcessItem.MessageTypeId != MDSMessageFactory.MessageTypeIdMDSDataTransferMessage) + { + return; + } + + //Create IncomingDataMessage from MDSDataTransferMessage + var dataMessage = new IncomingDataMessage(this, e.ProcessItem as MDSDataTransferMessage); + + try + { + //Check if client application registered to MessageReceived event. + if (MessageReceived == null) + { + dataMessage.Reject("Client application did not registered to MessageReceived event to receive messages."); + return; + } + + //Raise MessageReceived event + MessageReceived(this, new MessageReceivedEventArgs { Message = dataMessage }); + + //Check if client application acknowledged or rejected message + if (dataMessage.AckState != MessageAckStates.WaitingForAck) + { + return; + } + + //Check if auto acknowledge is active + if (!AutoAcknowledgeMessages) + { + dataMessage.Reject("Client application did not acknowledged or rejected message."); + return; + } + + //Auto acknowledge message + dataMessage.Acknowledge(); + } + catch + { + try + { + dataMessage.Reject("An unhandled exception occured during processing message by client application."); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + } + + /// + /// This method is called by _reconnectTimer_Tick to reconnect MDS server if disconnected. + /// + /// This argument is not used + private void ReconnectTimer_Tick(object state) + { + try + { + //Stop timer until method finishes + _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite); + + //Send Ping message if connected and needed to send a Ping message + if (_running && IsConnectedToServer()) + { + SendPingMessageIfNeeded(); + } + + //Reconnect if disconnected + if (_running && !IsConnectedToServer()) + { + ConnectAndRegister(); + } + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + finally + { + lock (_reconnectTimer) + { + if (_running) + { + //Restart timer + _reconnectTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + } + } + } + + /// + /// Sends a Ping message to MDS server if 60 seconds passed after last communication. + /// + private void SendPingMessageIfNeeded() + { + var now = DateTime.Now; + if (now.Subtract(LastIncomingMessageTime).TotalSeconds < 60 && + now.Subtract(LastOutgoingMessageTime).TotalSeconds < 60) + { + return; + } + + try + { + SendMessageInternal(new MDSPingMessage()); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + /// + /// Closes communication channel, thus disconnects from MDS server if it is connected. + /// + private void CloseCommunicationChannel() + { + try + { + _communicationChannel.Disconnect(); + } + catch (Exception ex) + { + Logger.Debug(ex.Message, ex); + } + } + + /// + /// Checks if client application is connected to MDS server. + /// + /// True, if connected. + private bool IsConnectedToServer() + { + return (_communicationChannel != null && _communicationChannel.State == CommunicationStates.Connected); + } + + #endregion + + #region Sub classes + + #region IncomingDataMessage class + + /// + /// Implements IIncomingMessage to send Ack/Reject via MDSClient. + /// + private class IncomingDataMessage : MDSDataTransferMessage, IIncomingMessage + { + /// + /// Acknowledgment state of the message. + /// + public MessageAckStates AckState { get; private set; } + + /// + /// Reference to the MDSClient object. + /// + private readonly MDSClient _client; + + /// + /// Creates a new IncomingDataMessage object from a MDSDataTransferMessage object. + /// + /// Reference to the MDSClient object + /// MDSDataTransferMessage object to create IncomingDataMessage + public IncomingDataMessage(MDSClient client, MDSDataTransferMessage message) + { + _client = client; + + DestinationApplicationName = message.DestinationApplicationName; + DestinationCommunicatorId = message.DestinationCommunicatorId; + DestinationServerName = message.DestinationServerName; + MessageData = message.MessageData; + MessageId = message.MessageId; + PassedServers = message.PassedServers; + RepliedMessageId = message.RepliedMessageId; + SourceApplicationName = message.SourceApplicationName; + SourceCommunicatorId = message.SourceCommunicatorId; + SourceServerName = message.SourceServerName; + TransmitRule = message.TransmitRule; + } + + /// + /// Used to Acknowledge this message. + /// Confirms that the message is received safely by client application. + /// A message must be acknowledged by client application to remove message from message queue. + /// MDS server doesn't send next message to the client application until the message is acknowledged. + /// Also, MDS server sends same message again if the message is not acknowledged in a certain time. + /// + public void Acknowledge() + { + if (AckState == MessageAckStates.Acknowledged) + { + return; + } + + if (AckState == MessageAckStates.Rejected) + { + throw new MDSException("Message is rejected before."); + } + + _client.LastAcknowledgedMessageId = MessageId; + + _client.SendMessageInternal( + new MDSOperationResultMessage + { + RepliedMessageId = MessageId, + Success = true + }); + + AckState = MessageAckStates.Acknowledged; + } + + /// + /// Used to reject (to not acknowledge) this message. + /// Indicates that the message can not received correctly or can not handled the message, and the message + /// must be sent to client application later again. + /// If MDS server gets reject for a message, + /// it doesn't send any message to the client application instance for a while. + /// If message is persistent, than it is sent to another instance of application or to same application instance again. + /// If message is not persistent, it is deleted. + /// + public void Reject() + { + Reject(""); + } + + /// + /// Used to reject (to not acknowledge) this message. + /// Indicates that the message can not received correctly or can not handled the message, and the message + /// must be sent to client application later again. + /// If MDS server gets reject for a message, + /// it doesn't send any message to the client application instance for a while. + /// If message is persistent, than it is sent to another instance of application or to same application instance again. + /// If message is not persistent, it is deleted. + /// + /// Reject reason + public void Reject(string reason) + { + if (AckState == MessageAckStates.Rejected) + { + return; + } + + if (AckState == MessageAckStates.Acknowledged) + { + throw new MDSException("Message is acknowledged before."); + } + + _client.SendMessageInternal( + new MDSOperationResultMessage + { + RepliedMessageId = MessageId, + Success = false, + ResultText = reason + }); + + AckState = MessageAckStates.Rejected; + } + + /// + /// Creates a empty response message for this message. + /// + /// Response message object + public IOutgoingMessage CreateResponseMessage() + { + return new OutgoingDataMessage(_client) + { + DestinationServerName = SourceServerName, + DestinationApplicationName = SourceApplicationName, + DestinationCommunicatorId = SourceCommunicatorId, + RepliedMessageId = MessageId, + TransmitRule = TransmitRule + }; + } + } + + #endregion + + #region OutgoingDataMessage class + + /// + /// Implements IOutgoingMessage to send message via MDSClient. + /// + private class OutgoingDataMessage : MDSDataTransferMessage, IOutgoingMessage + { + /// + /// Reference to the MDSClient object. + /// + private readonly MDSClient _client; + + /// + /// Creates a new OutgoingDataMessage object. + /// + /// Reference to the MDSClient object + public OutgoingDataMessage(MDSClient client) + { + _client = client; + } + + /// + /// Sends the message to the MDS server. + /// If this method does not throw an exception, + /// message is correctly delivered to MDS server (persistent message) + /// or to the destination application (non persistent message). + /// + public void Send() + { + Send(_client.DataMessageTimeout); + } + + /// + /// Sends the message to the MDS server. + /// If this method does not throw an exception, + /// message is correctly delivered to MDS server (persistent message) + /// or to the destination application (non persistent message). + /// + /// Timeout to send message as milliseconds + public void Send(int timeoutMilliseconds) + { + var reply = _client.SendAndWaitForReply( + this, + MDSMessageFactory.MessageTypeIdMDSOperationResultMessage, + timeoutMilliseconds + ) as MDSOperationResultMessage; //Timeout: 5 minutes or 1 minute. + + if (reply == null) + { + throw new MDSException("Can not send message. MessageId=(" + MessageId + ")."); + } + + if (!reply.Success) + { + throw new MDSException("Can not send message. MessageId=(" + MessageId + "). Detail: " + reply.ResultText); + } + } + + /// + /// Sends the message and waits for an incoming message for that message. + /// MDS can be used for Request/Response type messaging with this method. + /// Default timeout value: 5 minutes. + /// + /// Response message + public IIncomingMessage SendAndGetResponse() + { + return SendAndGetResponse(_client.DataMessageTimeout); + } + + /// + /// Sends the message and waits for an incoming message for that message. + /// MDS can be used for Request/Response type messaging with this method. + /// + /// Timeout to get response message as milliseconds + /// Response message + public IIncomingMessage SendAndGetResponse(int timeoutMilliseconds) + { + var response = _client.SendAndWaitForReply( + this, + MDSMessageFactory.MessageTypeIdMDSDataTransferMessage, + timeoutMilliseconds + ) as MDSDataTransferMessage; + var message = new IncomingDataMessage(_client, response); + if (_client.AutoAcknowledgeMessages) + { + message.Acknowledge(); + } + + return message; + } + } + + #endregion + + #region WaitingMessage class + + /// + /// This class is used as item in _waitingMessages collection. + /// Key: Message ID to wait response. + /// Value: ManualResetEvent to wait thread until response received. + /// + private class WaitingMessage + { + /// + /// What type of message is being waited. + /// For MDSOperationResultMessage, it is MDSMessageFactory.MessageTypeIdMDSOperationResultMessage. + /// For MDSDataTransferMessage, it is MDSMessageFactory.MessageTypeIdMDSDataTransferMessage. + /// + public int WaitingResponseType { get; private set; } + + /// + /// Response message received for sent message + /// This message may be MDSOperationResultMessage + /// or MDSDataTransferMessage according to WaitingResponseType. + /// + public MDSMessage ResponseMessage { get; set; } + + /// + /// ManualResetEvent to wait thread until response received. + /// + public ManualResetEvent WaitEvent { get; private set; } + + /// + /// State of the message. + /// + public WaitingMessageStates State { get; set; } + + /// + /// Creates a new WaitingMessage. + /// + public WaitingMessage(int waitingResponseType) + { + WaitingResponseType = waitingResponseType; + WaitEvent = new ManualResetEvent(false); + State = WaitingMessageStates.WaitingForResponse; + } + } + + public enum WaitingMessageStates + { + WaitingForResponse, + ClientDisconnected, + MessageRejected, + ResponseReceived + } + + #endregion + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/MDSRemoteAppEndPoint.cs b/src/MDSCommonLib/Client/MDSRemoteAppEndPoint.cs new file mode 100644 index 0000000..310eccf --- /dev/null +++ b/src/MDSCommonLib/Client/MDSRemoteAppEndPoint.cs @@ -0,0 +1,53 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client +{ + public class MDSRemoteAppEndPoint + { + public string ServerName { get; set; } + + public string ApplicationName { get; set; } + + public long CommunicatorId { get; set; } + + public MDSRemoteAppEndPoint() + { + + } + + public MDSRemoteAppEndPoint(string applicationName) + { + ApplicationName = applicationName; + } + + public MDSRemoteAppEndPoint(string serverName, string applicationName) + { + ServerName = serverName; + ApplicationName = applicationName; + } + + public MDSRemoteAppEndPoint(string serverName, string applicationName, long communicatorId) + { + ServerName = serverName; + ApplicationName = applicationName; + CommunicatorId = communicatorId; + } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeMessage.cs b/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeMessage.cs new file mode 100644 index 0000000..871ce2f --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeMessage.cs @@ -0,0 +1,46 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Client.MDSServices +{ + /// + /// This message is sent to invoke a method of an application that implements MDSService API. + /// It is sent by MDSServiceProxyBase class and received by MDSServiceApplication class. + /// + [Serializable] + public class MDSRemoteInvokeMessage + { + /// + /// Name of the target service class. + /// + public string ServiceClassName { get; set; } + + /// + /// Method of remote application to invoke. + /// + public string MethodName { get; set; } + + /// + /// Parameters of method. + /// + public object[] Parameters { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeReturnMessage.cs b/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeReturnMessage.cs new file mode 100644 index 0000000..c698e06 --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSRemoteInvokeReturnMessage.cs @@ -0,0 +1,44 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Exceptions; + +namespace MDS.Client.MDSServices +{ + /// + /// This message is sent as return message of a MDSRemoteInvokeMessage. + /// It is used to send return value of method invocation. + /// It is sent by MDSServiceApplication class and received by MDSServiceProxyBase class. + /// + [Serializable] + public class MDSRemoteInvokeReturnMessage + { + /// + /// Return value of remote method invocation. + /// + public object ReturnValue { get; set; } + + /// + /// If any exception occured during method invocation, this field contains Exception object. + /// If no exception occured, this field is null. + /// + public MDSRemoteException RemoteException { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSService.cs b/src/MDSCommonLib/Client/MDSServices/MDSService.cs new file mode 100644 index 0000000..a98a67e --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSService.cs @@ -0,0 +1,321 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using System.Text; + +namespace MDS.Client.MDSServices +{ + /// + /// Any MDSService class must be inherited from this class. + /// + public abstract class MDSService + { + #region Public properties + + /// + /// When a method of a MDSService application is invoked, this field stores address of source application in MDS. + /// + public MDSRemoteAppEndPoint RemoteApplication { get; internal set; } + + /// + /// When a method of a MDSService application is invoked, this field stores the original message that is sent by MDS server. + /// + public IIncomingMessage IncomingMessage { get; internal set; } + + #endregion + + #region Predefined Public Service Methods + + /// + /// This method generates client proxy class to use this service. + /// It is also a MDSServiceMethod, so, clients can update it's proxy classes via calling this method remotely. + /// + /// Namespace of generating proxy class + /// Proxy class code to use this service + [MDSServiceMethod(Description = "This method generates client proxy class to use this service. Clients can update it's proxy classes via calling this method remotely.")] + [return: MDSServiceMethodParameter("Proxy class code to use this service")] + public string GenerateProxyClass([MDSServiceMethodParameter("Namespace of generating proxy class")] string namespaceName) + { + //Check parameters + if (string.IsNullOrEmpty(namespaceName)) + { + namespaceName = "MDSServiceProxies"; + } + + //Get this Type, Methods and Attributes + var serviceType = GetType(); + var methods = serviceType.GetMethods(); + var attributes = serviceType.GetCustomAttributes(typeof(MDSServiceAttribute), true); + + //Check for MDSService attribute + if (attributes.Length <= 0) + { + return "This class has not MDSService attribute. So, it is not a MDSService."; + } + + //Get MDSService attribute + var mdsServiceAttribute = (MDSServiceAttribute)attributes[0]; + + //Generate class name + var proxyClassName = serviceType.Name + "Proxy"; + + //Start generating code + var classBuilder = new StringBuilder(); + + //Generate header of file. + classBuilder.AppendLine("/* This code file is generated by MDSService Proxy Generator tool."); + classBuilder.AppendLine(" * "); + classBuilder.AppendLine(" * Service Name : " + serviceType.Name); + classBuilder.AppendLine(" * Service version : " + mdsServiceAttribute.Version); + classBuilder.AppendLine(" * Generating date : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + classBuilder.AppendLine(" */"); + classBuilder.AppendLine(); + + //Namespaces + classBuilder.AppendLine("using System;"); + classBuilder.AppendLine("using MDS.Client;"); + classBuilder.AppendLine("using MDS.Client.MDSServices;"); + classBuilder.AppendLine(); + + //Class code + classBuilder.AppendLine("namespace " + namespaceName); + classBuilder.AppendLine("{"); + classBuilder.AppendLine(" /// "); + classBuilder.AppendLine(" /// This class is a proxy class to use " + serviceType.Name + " service."); + if (!string.IsNullOrEmpty(mdsServiceAttribute.Description)) + { + classBuilder.AppendLine(" /// Service Description: " + mdsServiceAttribute.Description); + } + + classBuilder.AppendLine(" /// "); + classBuilder.AppendLine(" public partial class " + proxyClassName + " : MDSServiceProxyBase"); + classBuilder.AppendLine(" {"); + + //Constructor + classBuilder.AppendLine(" #region Constructor"); + classBuilder.AppendLine(" "); + classBuilder.AppendLine(" /// "); + classBuilder.AppendLine(" /// Creates a new instance of " + proxyClassName + "."); + classBuilder.AppendLine(" /// "); + classBuilder.AppendLine(" /// Reference to a MDSServiceConsumer object to send/receive MDS messages"); + classBuilder.AppendLine(" /// Remote application end point to send requests"); + classBuilder.AppendLine(" public " + proxyClassName + "(MDSServiceConsumer serviceConsumer, MDSRemoteAppEndPoint remoteEndPoint)"); + classBuilder.AppendLine(" : base(serviceConsumer, remoteEndPoint, \"" + serviceType.Name + "\")"); + classBuilder.AppendLine(" {"); + classBuilder.AppendLine(" "); + classBuilder.AppendLine(" }"); + classBuilder.AppendLine(" "); + classBuilder.AppendLine(" #endregion"); + classBuilder.AppendLine(" "); + + //Methods + classBuilder.AppendLine(" #region " + serviceType.Name + " methods"); + classBuilder.AppendLine(" "); + foreach (var method in methods) + { + if (IsPredefinedMethod(method.Name)) + { + continue; + } + + WriteMethod(classBuilder, method); + } + + classBuilder.AppendLine(" #endregion"); + classBuilder.AppendLine(" "); + classBuilder.AppendLine(" #region Default (predefined) service methods"); + classBuilder.AppendLine(" "); + foreach (var method in methods) + { + if (!IsPredefinedMethod(method.Name)) + { + continue; + } + + WriteMethod(classBuilder, method); + } + + classBuilder.AppendLine(" #endregion"); + + //Close class + classBuilder.AppendLine(" }"); + + //Close namespace + classBuilder.AppendLine("}"); + + return classBuilder.ToString(); + } + + /// + /// This method can be used to check if service is available. + /// + /// A string message + /// Reply to message as formatted: "RE:message". + [MDSServiceMethod(Description = "This method can be used to check if service is available.")] + [return: MDSServiceMethodParameter("Reply to message as formatted: 'RE: message'")] + public string CheckServiceIsAvailable([MDSServiceMethodParameter("A message to reply")] string message) + { + return ("RE: " + message); + } + + #endregion + + #region Private methods + + private static void WriteMethod(StringBuilder classBuilder, MethodInfo method) + { + //Check for MDSServiceMethod attribute + var methodAttributes = method.GetCustomAttributes(typeof(MDSServiceMethodAttribute), true); + if (methodAttributes.Length <= 0) + { + return; + } + + //Get MDSServiceMethod attribute + var serviceMethodAttribute = (MDSServiceMethodAttribute)methodAttributes[0]; + + //Get return type + var returnType = NormalizeType(method.ReturnType.Name); + + //Get parameters + var parameters = method.GetParameters(); + + //Generate proxy method arguments and invoke method parameters + var methodArgumentsString = new StringBuilder(); + var invokeParameters = new StringBuilder(); + foreach (var parameter in parameters) + { + var paramType = NormalizeType(parameter.ParameterType.Name); + if (methodArgumentsString.Length > 0) + { + methodArgumentsString.Append(", "); + } + + methodArgumentsString.Append(paramType + " " + parameter.Name); + invokeParameters.Append(", " + parameter.Name); + } + + //Generate method summary + classBuilder.AppendLine(" /// "); + classBuilder.AppendLine(" /// " + (serviceMethodAttribute.Description ?? "No method summary available.")); + classBuilder.AppendLine(" /// "); + + //Generate XML-Comments for parameters + foreach (var parameter in parameters) + { + var paramAttributes = parameter.GetCustomAttributes(typeof(MDSServiceMethodParameterAttribute), true); + if (paramAttributes.Length <= 0) + { + continue; + } + + classBuilder.AppendLine(" /// " + ((MDSServiceMethodParameterAttribute)paramAttributes[0]).Description + ""); + } + + //Generate XML-Comments for return value + if (returnType != "void") + { + var returnAttributes = method.ReturnParameter.GetCustomAttributes(typeof(MDSServiceMethodParameterAttribute), true); + if (returnAttributes.Length > 0) + { + classBuilder.AppendLine(" /// " + ((MDSServiceMethodParameterAttribute)returnAttributes[0]).Description + ""); + } + } + + //Generate method signature and opening bracket + classBuilder.AppendLine(" public " + returnType + " " + method.Name + "(" + methodArgumentsString + ")"); + classBuilder.AppendLine(" {"); + + //Generate method body according to return value + if (returnType == "void") + { + classBuilder.AppendLine(" InvokeRemoteMethod(\"" + method.Name + "\"" + invokeParameters + ");"); + } + else + { + classBuilder.AppendLine(" return (" + returnType + ") InvokeRemoteMethodAndGetResult(\"" + method.Name + "\"" + invokeParameters + ");"); + } + + //Method closing bracket + classBuilder.AppendLine(" }"); + classBuilder.AppendLine(" "); + } + + /// + /// Normalizes some known primitive types. + /// + /// Type name + /// Normalized type name + private static string NormalizeType(string typeName) + { + switch (typeName) + { + case "Void": + return "void"; + case "Boolean": + return "bool"; + case "Byte": + return "byte"; + case "Byte[]": + return "byte[]"; + case "Int16": + return "short"; + case "Int16[]": + return "short[]"; + case "Int32": + return "int"; + case "Int32[]": + return "int[]"; + case "Int64": + return "long"; + case "Int64[]": + return "long[]"; + case "String": + return "string"; + case "String[]": + return "string[]"; + case "Single": + return "float"; + case "Single[]": + return "float[]"; + case "Double": + return "double"; + case "Double[]": + return "double[]"; + } + + //Not known type + return typeName; + } + + /// + /// Checks if a method is predefined method (MDSService methods in MDSService class). + /// + /// Method name to check + /// True: Yes, it is.. + private static bool IsPredefinedMethod(string methodName) + { + return (methodName == "GenerateProxyClass" || methodName == "CheckServiceIsAvailable"); + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceApplication.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceApplication.cs new file mode 100644 index 0000000..039e0ad --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceApplication.cs @@ -0,0 +1,435 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using MDS.Communication; +using MDS.Communication.Messages; +using MDS.Exceptions; + +namespace MDS.Client.MDSServices +{ + /// + /// This class ensures to use a class that is derived from MDSService class, as a service on MDS. + /// + public class MDSServiceApplication : IDisposable + { + #region Private fields + + /// + /// Underlying MDSClient object to send/receive MDS messages. + /// + private readonly MDSClient _mdsClient; + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The service object that handles all method invokes. + /// + private SortedList _serviceObjects; + + #endregion + + #region Constructors + + /// + /// Creates a new MDSServiceApplication object with default values to connect to MDS server. + /// + /// Name of the application + public MDSServiceApplication(string applicationName) + { + _mdsClient = new MDSClient(applicationName, CommunicationConsts.DefaultIpAddress, CommunicationConsts.DefaultMDSPort); + Initialize(); + } + + /// + /// Creates a new MDSServiceApplication object with default port to connect to MDS server. + /// + /// Name of the application + /// IP address of MDS server + public MDSServiceApplication(string applicationName, string ipAddress) + { + _mdsClient = new MDSClient(applicationName, ipAddress, CommunicationConsts.DefaultMDSPort); + Initialize(); + } + + /// + /// Creates a new MDSServiceApplication object. + /// + /// Name of the application + /// IP address of MDS server + /// TCP port of MDS server + public MDSServiceApplication(string applicationName, string ipAddress, int port) + { + _mdsClient = new MDSClient(applicationName, ipAddress, port); + Initialize(); + } + + #endregion + + #region Public methods + + /// + /// This method connects to MDS server using underlying MDSClient object. + /// + public void Connect() + { + _mdsClient.Connect(); + } + + /// + /// This method disconnects from MDS server using underlying MDSClient object. + /// + public void Disconnect() + { + _mdsClient.Disconnect(); + } + + /// + /// Adds a new MDSService for this service application. + /// + /// Service to add + public void AddService(MDSService service) + { + if (service == null) + { + throw new ArgumentNullException("service"); + } + + var type = service.GetType(); + var attributes = type.GetCustomAttributes(typeof (MDSServiceAttribute), true); + if(attributes.Length <= 0) + { + throw new MDSException("Service class must has MDSService attribute to be added."); + } + + if (_serviceObjects.ContainsKey(type.Name)) + { + throw new MDSException("Service '" + type.Name + "' is already added."); + } + + _serviceObjects.Add(type.Name, new ServiceObject(service, (MDSServiceAttribute)attributes[0])); + } + + /// + /// Removes a MDSService from this service application. + /// + /// Service to add + public void RemoveService(MDSService service) + { + if (service == null) + { + throw new ArgumentNullException("service"); + } + + var type = service.GetType(); + if (!_serviceObjects.ContainsKey(type.Name)) + { + return; + } + + _serviceObjects.Remove(type.Name); + } + + /// + /// Disposes this object, disposes/closes underlying MDSClient object. + /// + public void Dispose() + { + _mdsClient.Dispose(); + } + + #endregion + + #region Private methods + + /// + /// Initializes this object. + /// + private void Initialize() + { + _serviceObjects = new SortedList(); + _mdsClient.MessageReceived += MdsClient_MessageReceived; + } + + /// + /// This method handles all incoming messages from MDS server. + /// + /// Creator object of method (MDSClient object) + /// Message event arguments + private void MdsClient_MessageReceived(object sender, MessageReceivedEventArgs e) + { + //Deserialize message + MDSRemoteInvokeMessage invokeMessage; + try + { + invokeMessage = (MDSRemoteInvokeMessage)GeneralHelper.DeserializeObject(e.Message.MessageData); + } + catch (Exception ex) + { + AcknowledgeMessage(e.Message); + SendException(e.Message, new MDSRemoteException("Incoming message can not be deserialized to MDSRemoteInvokeMessage.", ex)); + return; + } + + //Check service class name + if (!_serviceObjects.ContainsKey(invokeMessage.ServiceClassName)) + { + AcknowledgeMessage(e.Message); + SendException(e.Message, new MDSRemoteException("There is no service with name '" + invokeMessage.ServiceClassName + "'")); + return; + } + + //Get service object + var serviceObject = _serviceObjects[invokeMessage.ServiceClassName]; + + //Invoke service method and get return value + object returnValue; + try + { + //Set request variables to service object and invoke method + serviceObject.Service.IncomingMessage = e.Message; + returnValue = serviceObject.InvokeMethod(invokeMessage.MethodName, invokeMessage.Parameters); + } + catch (Exception ex) + { + SendException(e.Message, + new MDSRemoteException( + ex.Message + Environment.NewLine + "Service Class: " + + invokeMessage.ServiceClassName + " " + Environment.NewLine + + "Service Version: " + serviceObject.ServiceAttribute.Version, ex)); + return; + } + + //Send return value to sender application + SendReturnValue(e.Message, returnValue); + } + + + /// + /// Sends an Exception to the remote application that invoked a service method + /// + /// Incoming invoke message from remote application + /// Exception to send + private static void SendException(IIncomingMessage incomingMessage, MDSRemoteException exception) + { + if (incomingMessage.TransmitRule != MessageTransmitRules.DirectlySend) + { + return; + } + + try + { + //Create return message + var returnMessage = new MDSRemoteInvokeReturnMessage { RemoteException = exception }; + //Create response message and send + var responseMessage = incomingMessage.CreateResponseMessage(); + responseMessage.MessageData = GeneralHelper.SerializeObject(returnMessage); + responseMessage.Send(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// Sends return value to the remote application that invoked a service method. + /// + /// Incoming invoke message from remote application + /// Return value to send + private static void SendReturnValue(IIncomingMessage incomingMessage, object returnValue) + { + if (incomingMessage.TransmitRule != MessageTransmitRules.DirectlySend) + { + return; + } + + try + { + //Create return message + var returnMessage = new MDSRemoteInvokeReturnMessage { ReturnValue = returnValue }; + //Create response message and send + var responseMessage = incomingMessage.CreateResponseMessage(); + responseMessage.MessageData = GeneralHelper.SerializeObject(returnMessage); + responseMessage.Send(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// Acknowledges a message. + /// + /// Message to acknowledge + private static void AcknowledgeMessage(IIncomingMessage message) + { + try + { + message.Acknowledge(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + #endregion + + #region Sub classes + + /// + /// Represents a MDSService object. + /// + private class ServiceObject + { + /// + /// The service object that is used to invoke methods on. + /// + public MDSService Service { get; private set; } + + /// + /// MDSService attribute of Service object's class. + /// + public MDSServiceAttribute ServiceAttribute { get; private set; } + + /// + /// Name of the Service object's class. + /// + private readonly string _serviceClassName; + + /// + /// This collection stores a list of all methods of T, if that can be invoked from remote applications or not. + /// Key: Method name + /// Value: True, if it can be invoked from remote application. + /// + private readonly SortedList _methods; + + /// + /// Creates a new ServiceObject. + /// + /// The service object that is used to invoke methods on + /// MDSService attribute of service object's class + public ServiceObject(MDSService service, MDSServiceAttribute serviceAttribute) + { + Service = service; + ServiceAttribute = serviceAttribute; + + _serviceClassName = service.GetType().Name; + + //Find all methods + _methods = new SortedList(); + foreach (var methodInfo in Service.GetType().GetMethods()) + { + var attributes = methodInfo.GetCustomAttributes(typeof(MDSServiceMethodAttribute), true); + _methods.Add(methodInfo.Name, attributes.Length > 0); + } + } + + /// + /// Invokes a method of Service object. + /// + /// Name of the method to invoke + /// Parameters of method + /// Return value of method + public object InvokeMethod(string methodName, params object[] parameters) + { + //Check if there is a method with name methodName + if (!_methods.ContainsKey(methodName)) + { + AcknowledgeMessage(Service.IncomingMessage); + throw new MethodAccessException("There is not a method with name '" + methodName + "' in service '" + _serviceClassName + "'."); + } + + //Check if method has MDSServiceMethod attribute + if (!_methods[methodName]) + { + AcknowledgeMessage(Service.IncomingMessage); + throw new MethodAccessException("Method '" + methodName + "' has not MDSServiceMethod attribute. It can not be invoked from remote applications."); + } + + //Set object properties before method invocation + Service.RemoteApplication = + new MDSRemoteAppEndPoint(Service.IncomingMessage.SourceServerName, + Service.IncomingMessage.SourceApplicationName, + Service.IncomingMessage.SourceCommunicatorId); + + //If Transmit rule is DirectlySend than message must be acknowledged now. + //If any exception occurs on method invocation, exception will be sent to + //remote application in a MDSRemoteInvokeReturnMessage. + if (Service.IncomingMessage.TransmitRule == MessageTransmitRules.DirectlySend) + { + AcknowledgeMessage(Service.IncomingMessage); + } + + //Invoke method and get return value + try + { + var returnValue = Service.GetType().GetMethod(methodName).Invoke(Service, parameters); + if (Service.IncomingMessage.TransmitRule != MessageTransmitRules.DirectlySend) + { + AcknowledgeMessage(Service.IncomingMessage); + } + + return returnValue; + } + catch (Exception ex) + { + if (Service.IncomingMessage.TransmitRule != MessageTransmitRules.DirectlySend) + { + RejectMessage(Service.IncomingMessage, ex.Message); + } + + if (ex.InnerException != null) + { + throw ex.InnerException; + } + + throw; + } + } + + /// + /// Rejects a message. + /// + /// Message to reject + /// Reject reason + private static void RejectMessage(IIncomingMessage message, string reason) + { + try + { + message.Reject(reason); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceAttribute.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceAttribute.cs new file mode 100644 index 0000000..9729f2f --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceAttribute.cs @@ -0,0 +1,50 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Client.MDSServices +{ + /// + /// Any MDSService class must has this attribute. + /// + [AttributeUsage(AttributeTargets.Class)] + public class MDSServiceAttribute : Attribute + { + /// + /// A brief description of Service. + /// + public string Description { get; set; } + + /// + /// Service Version. This property can be used to indicate the code version (especially the version of service methods). + /// This value is sent to user application on an exception, so, user/client application can know that service version is changed. + /// Default value: NO_VERSION. + /// + public string Version { get; set; } + + /// + /// Creates a new MDSServiceAttribute object. + /// + public MDSServiceAttribute() + { + Version = "NO_VERSION"; + } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceConsumer.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceConsumer.cs new file mode 100644 index 0000000..7ac4848 --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceConsumer.cs @@ -0,0 +1,111 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication; + +namespace MDS.Client.MDSServices +{ + public class MDSServiceConsumer : IDisposable + { + #region Public fields + + /// + /// Underlying MDSClient object to send/receive MDS messages. + /// + internal MDSClient MdsClient { get; private set; } + + #endregion + + #region Constructors + + /// + /// Creates a new MDSServiceApplication object with default values to connect to MDS server. + /// + /// Name of the application + public MDSServiceConsumer(string applicationName) + { + MdsClient = new MDSClient(applicationName, CommunicationConsts.DefaultIpAddress, CommunicationConsts.DefaultMDSPort); + Initialize(); + } + + /// + /// Creates a new MDSServiceApplication object with default port to connect to MDS server. + /// + /// Name of the application + /// IP address of MDS server + public MDSServiceConsumer(string applicationName, string ipAddress) + { + MdsClient = new MDSClient(applicationName, ipAddress, CommunicationConsts.DefaultMDSPort); + } + + /// + /// Creates a new MDSServiceApplication object. + /// + /// Name of the application + /// IP address of MDS server + /// TCP port of MDS server + public MDSServiceConsumer(string applicationName, string ipAddress, int port) + { + MdsClient = new MDSClient(applicationName, ipAddress, port); + } + + #endregion + + #region Public methods + + /// + /// This method connects to MDS server using underlying MDSClient object. + /// + public void Connect() + { + MdsClient.Connect(); + } + + /// + /// This method disconnects from MDS server using underlying MDSClient object. + /// + public void Disconnect() + { + MdsClient.Disconnect(); + } + + /// + /// Disposes this object, disposes/closes underlying MDSClient object. + /// + public void Dispose() + { + MdsClient.Dispose(); + } + + #endregion + + #region Private methods + + /// + /// Initializes this object. + /// + private void Initialize() + { + MdsClient.CommunicationWay = CommunicationWays.Send; + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodAttribute.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodAttribute.cs new file mode 100644 index 0000000..0e1a097 --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodAttribute.cs @@ -0,0 +1,36 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Client.MDSServices +{ + /// + /// Any MDSService class must use this attribute on it's remote methods. + /// If a method has not MDSServiceMethod attribute, it can not be invoked by remote applications. + /// + [AttributeUsage(AttributeTargets.Method)] + public class MDSServiceMethodAttribute : Attribute + { + /// + /// A brief description (and may be usage) of method. + /// + public string Description { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodParameterAttribute.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodParameterAttribute.cs new file mode 100644 index 0000000..1c4881f --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceMethodParameterAttribute.cs @@ -0,0 +1,44 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Client.MDSServices +{ + /// + /// This attribute is used to add information to a parameter or return value of a MDSServiceMethod. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)] + public class MDSServiceMethodParameterAttribute : Attribute + { + /// + /// A brief description of parameter. + /// + public string Description { get; set; } + + /// + /// Creates a new MDSServiceMethodParameterAttribute. + /// + /// + public MDSServiceMethodParameterAttribute(string description) + { + Description = description; + } + } +} diff --git a/src/MDSCommonLib/Client/MDSServices/MDSServiceProxyBase.cs b/src/MDSCommonLib/Client/MDSServices/MDSServiceProxyBase.cs new file mode 100644 index 0000000..54fa552 --- /dev/null +++ b/src/MDSCommonLib/Client/MDSServices/MDSServiceProxyBase.cs @@ -0,0 +1,166 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; + +namespace MDS.Client.MDSServices +{ + /// + /// This is the base class for proxy classes that is used to make remote invocation to a MDSService. + /// + public abstract class MDSServiceProxyBase + { + /// + /// Reference to the underlying MDSServiceConsumer object to send/receive MDS messages. + /// + private readonly MDSServiceConsumer _serviceConsumer; + + /// + /// MDS Address of remote application. + /// + public MDSRemoteAppEndPoint RemoteApplication { get; private set; } + + /// + /// Transmit rule of underlying messages. + /// Using this peoperty, connection between applications can be changes from tight coupled to loose coupled and vice versa. + /// Just methods whose return type is void, can use other transmit rule than DirectlySend. So, that methods may be loose coupled by setting transmit rule. + /// Methods that has return value always use DirectlySend transmit rule, even if it is set by user to another rule. So, that methods must be tight coupled. + /// Default value: DirectlySend. + /// + public MessageTransmitRules TransmitRule { get; set; } + + /// + /// Timeout for service method calls as milliseconds. + /// Default: 300000 (5 minutes). + /// + public int Timeout { get; set; } + + /// + /// Name of the service class. + /// + private readonly string _serviceClassName; + + /// + /// Initializes MDSServiceProxyBase. + /// + /// Reference to a MDSServiceConsumer object to send/receive MDS messages + /// Address of remote application + /// Name of the service class + protected MDSServiceProxyBase(MDSServiceConsumer serviceConsumer, MDSRemoteAppEndPoint remoteEndPoint, string serviceClassName) + { + if (string.IsNullOrEmpty(serviceClassName)) + { + throw new ArgumentNullException("serviceClassName"); + } + + if (remoteEndPoint == null) + { + remoteEndPoint = new MDSRemoteAppEndPoint(); + } + + _serviceConsumer = serviceConsumer; + RemoteApplication = remoteEndPoint; + _serviceClassName = serviceClassName; + TransmitRule = MessageTransmitRules.DirectlySend; + Timeout = 300000; + } + + /// + /// Sends remote method invocation message to the remote application and gets result. + /// This simplifies remove method invocation like calling a method locally. + /// It throws Exception if any Exception occured on remote application's method. + /// + /// Method name to invoke + /// Method parameters + /// Return value of remote method + protected object InvokeRemoteMethodAndGetResult(string methodName, params object[] args) + { + //Create MDSRemoteInvokeMessage object that contains invocation informations + var invokeMessage = new MDSRemoteInvokeMessage { ServiceClassName = _serviceClassName, MethodName = methodName, Parameters = args }; + + //Create MDS message to transmit MDSRemoteInvokeMessage. + var outgoingMessage = _serviceConsumer.MdsClient.CreateMessage(); + outgoingMessage.DestinationServerName = RemoteApplication.ServerName; + outgoingMessage.DestinationApplicationName = RemoteApplication.ApplicationName; + outgoingMessage.DestinationCommunicatorId = RemoteApplication.CommunicatorId; + outgoingMessage.TransmitRule = MessageTransmitRules.DirectlySend; + outgoingMessage.MessageData = GeneralHelper.SerializeObject(invokeMessage); + + //Send message and get response + var incomingMessage = outgoingMessage.SendAndGetResponse(Timeout); + incomingMessage.Acknowledge(); + + //Deserialize and check return value + var invokeReturnMessage = (MDSRemoteInvokeReturnMessage) GeneralHelper.DeserializeObject(incomingMessage.MessageData); + if (invokeReturnMessage.RemoteException != null) + { + throw invokeReturnMessage.RemoteException; + } + + //Success + return invokeReturnMessage.ReturnValue; + } + + /// + /// Sends remote method invocation message to the remote application and gets result. + /// This simplifies remove method invocation like calling a method locally. + /// It throws Exception if any Exception occured on remote application's method. + /// + /// Method name to invoke + /// Method parameters + protected void InvokeRemoteMethod(string methodName, params object[] args) + { + //Create MDSRemoteInvokeMessage object that contains invocation informations + var invokeMessage = new MDSRemoteInvokeMessage + { + ServiceClassName = _serviceClassName, + MethodName = methodName, + Parameters = args + }; + + //Create MDS message to transmit MDSRemoteInvokeMessage. + var outgoingMessage = _serviceConsumer.MdsClient.CreateMessage(); + outgoingMessage.DestinationServerName = RemoteApplication.ServerName; + outgoingMessage.DestinationApplicationName = RemoteApplication.ApplicationName; + outgoingMessage.DestinationCommunicatorId = RemoteApplication.CommunicatorId; + outgoingMessage.TransmitRule = TransmitRule; + outgoingMessage.MessageData = GeneralHelper.SerializeObject(invokeMessage); + + if (TransmitRule == MessageTransmitRules.DirectlySend) + { + //Send message and get response + var incomingMessage = outgoingMessage.SendAndGetResponse(Timeout); + incomingMessage.Acknowledge(); + + //Deserialize and check return value + var invokeReturnMessage = (MDSRemoteInvokeReturnMessage)GeneralHelper.DeserializeObject(incomingMessage.MessageData); + if (invokeReturnMessage.RemoteException != null) + { + throw invokeReturnMessage.RemoteException; + } + } + else + { + //Just send message + outgoingMessage.Send(); + } + } + } +} diff --git a/src/MDSCommonLib/Client/MessageAckStates.cs b/src/MDSCommonLib/Client/MessageAckStates.cs new file mode 100644 index 0000000..0dffba7 --- /dev/null +++ b/src/MDSCommonLib/Client/MessageAckStates.cs @@ -0,0 +1,42 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client +{ + /// + /// Respesents states of an incoming message. + /// + public enum MessageAckStates + { + /// + /// Message is waiting for Ack. + /// + WaitingForAck, + + /// + /// Message is acknowledged. + /// + Acknowledged, + + /// + /// Message is rejected. + /// + Rejected + } +} diff --git a/src/MDSCommonLib/Client/MessageReceivedEventArgs.cs b/src/MDSCommonLib/Client/MessageReceivedEventArgs.cs new file mode 100644 index 0000000..967b59f --- /dev/null +++ b/src/MDSCommonLib/Client/MessageReceivedEventArgs.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Client +{ + /// + /// A delegate to create events when a data transfer message received from MDS server. + /// + /// The object which raises event + /// Event arguments + public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e); + + /// + /// Stores message informations. + /// + public class MessageReceivedEventArgs : EventArgs + { + /// + /// Received message from MDS server. + /// + public IIncomingMessage Message { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/WebServices/IWebServiceIncomingMessage.cs b/src/MDSCommonLib/Client/WebServices/IWebServiceIncomingMessage.cs new file mode 100644 index 0000000..19d851c --- /dev/null +++ b/src/MDSCommonLib/Client/WebServices/IWebServiceIncomingMessage.cs @@ -0,0 +1,91 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Client.WebServices +{ + /// + /// Represents an incoming data message to a MDS Web Service from MDS server. + /// + public interface IWebServiceIncomingMessage + { + #region Properties + + /// + /// Gets the unique identifier for this message. + /// + string MessageId { get; } + + /// + /// Name of the first source server of the message. + /// + string SourceServerName { get; } + + /// + /// Name of the sender application of the message. + /// + string SourceApplicationName { get; } + + /// + /// The source communication channel's (Communicator's) unique Id. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicate a spesific communicator. + /// This field is set by MDS automatically. + /// + long SourceCommunicatorId { get; } + + /// + /// Name of the final destination server of the message. + /// + string DestinationServerName { get; } + + /// + /// Name of the final destination application of the message. + /// + string DestinationApplicationName { get; } + + /// + /// Passed servers of message until now, includes source and destination servers. + /// + ServerTransmitReport[] PassedServers { get; } + + /// + /// Essential application message data that is received. + /// + byte[] MessageData { get; } + + /// + /// Transmit rule of message. + /// This is important because it determines persistence and transmit time of message. + /// Default: StoreAndForward. + /// + MessageTransmitRules TransmitRule { get; } + + #endregion + + #region Methods + + IWebServiceResponseMessage CreateResponseMessage(); + + IWebServiceOutgoingMessage CreateResponseDataMessage(); + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/WebServices/IWebServiceOperationResultMessage.cs b/src/MDSCommonLib/Client/WebServices/IWebServiceOperationResultMessage.cs new file mode 100644 index 0000000..cbeb2e1 --- /dev/null +++ b/src/MDSCommonLib/Client/WebServices/IWebServiceOperationResultMessage.cs @@ -0,0 +1,38 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.WebServices +{ + /// + /// Represents a result message for an incoming message to MDS Web Service. + /// + public interface IWebServiceOperationResultMessage + { + /// + /// Operation result. + /// True, if operation is successful. + /// + bool Success { get; set; } + + /// + /// A text that may be used as a description for result of operation. + /// + string ResultText { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/WebServices/IWebServiceOutgoingMessage.cs b/src/MDSCommonLib/Client/WebServices/IWebServiceOutgoingMessage.cs new file mode 100644 index 0000000..ad87961 --- /dev/null +++ b/src/MDSCommonLib/Client/WebServices/IWebServiceOutgoingMessage.cs @@ -0,0 +1,36 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.WebServices +{ + /// + /// Represents an outgoing data message from a MDS web service to MDS server. + /// + public interface IWebServiceOutgoingMessage + { + #region Properties + + /// + /// Essential application message data to be sent. + /// + byte[] MessageData { get; set; } + + #endregion + } +} diff --git a/src/MDSCommonLib/Client/WebServices/IWebServiceResponseMessage.cs b/src/MDSCommonLib/Client/WebServices/IWebServiceResponseMessage.cs new file mode 100644 index 0000000..b7c1eeb --- /dev/null +++ b/src/MDSCommonLib/Client/WebServices/IWebServiceResponseMessage.cs @@ -0,0 +1,38 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Client.WebServices +{ + /// + /// Represents a response message to a IWebServiceIncomingMessage from a MDS web service. + /// + public interface IWebServiceResponseMessage + { + /// + /// Process result of IWebServiceIncomingMessage. + /// + IWebServiceOperationResultMessage Result { get; set; } + + /// + /// Response message to IWebServiceIncomingMessage. + /// This may be null to do not send a response to incoming message. + /// + IWebServiceOutgoingMessage Message { get; set; } + } +} diff --git a/src/MDSCommonLib/Client/WebServices/WebServiceHelper.cs b/src/MDSCommonLib/Client/WebServices/WebServiceHelper.cs new file mode 100644 index 0000000..51cee5e --- /dev/null +++ b/src/MDSCommonLib/Client/WebServices/WebServiceHelper.cs @@ -0,0 +1,183 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; +using MDS.Serialization; + +namespace MDS.Client.WebServices +{ + /// + /// This class is used by MDS Web Services to serialize/deserialize/create messages. + /// + public static class WebServiceHelper + { + #region Public methods + + /// + /// Deserializes an incoming message for Web Service from MDS server. + /// + /// Message as byte array + /// Deserialized message + public static IWebServiceIncomingMessage DeserializeMessage(byte[] bytesOfMessage) + { + var dataMessage = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), bytesOfMessage); + return new IncomingDataMessage(dataMessage); + } + + /// + /// Serializes a message to send to MDS server from Web Service. + /// + /// Message to serialize + /// Serialized message + public static byte[] SerializeMessage(IWebServiceResponseMessage responseMessage) + { + CheckResponseMessage(responseMessage); + var response = ((ResponseMessage) responseMessage).CreateDataTransferResponseMessage(); + return MDSSerializationHelper.SerializeToByteArray(response); + } + + #endregion + + #region Private methods + + /// + /// Checks a response message whether it is a valid response message + /// + /// Message to check + private static void CheckResponseMessage(IWebServiceResponseMessage responseMessage) + { + if (responseMessage == null) + { + throw new ArgumentNullException("responseMessage", "responseMessage can not be null."); + } + + if (!(responseMessage is ResponseMessage)) + { + throw new Exception("responseMessage parameter is not known type."); + } + + if (responseMessage.Result == null) + { + throw new ArgumentNullException("responseMessage", "responseMessage.Result can not be null."); + } + + if( !(responseMessage.Result is ResultMessage)) + { + throw new Exception("responseMessage.Result is not known type."); + } + + if(responseMessage.Message != null && !(responseMessage.Message is OutgoingDataMessage)) + { + throw new Exception("responseMessage.Message is not known type."); + } + } + + #endregion + + #region Sub classes + + /// + /// Implements IWebServiceIncomingMessage to be used by MDS web service. + /// + private class IncomingDataMessage : MDSDataTransferMessage, IWebServiceIncomingMessage + { + /// + /// Creates a new IncomingDataMessage object from a MDSDataTransferMessage object. + /// + /// MDSDataTransferMessage object to create IncomingDataMessage + public IncomingDataMessage(MDSDataTransferMessage message) + { + DestinationApplicationName = message.DestinationApplicationName; + DestinationCommunicatorId = message.DestinationCommunicatorId; + DestinationServerName = message.DestinationServerName; + MessageData = message.MessageData; + MessageId = message.MessageId; + PassedServers = message.PassedServers; + RepliedMessageId = message.RepliedMessageId; + SourceApplicationName = message.SourceApplicationName; + SourceCommunicatorId = message.SourceCommunicatorId; + SourceServerName = message.SourceServerName; + TransmitRule = message.TransmitRule; + } + + /// + /// Creates IWebServiceResponseMessage using this incoming message, to return from web service to MDS server. + /// + /// Response message to this message + public IWebServiceResponseMessage CreateResponseMessage() + { + return new ResponseMessage { Result = new ResultMessage { RepliedMessageId = MessageId } }; + } + + /// + /// Creates IWebServiceOutgoingMessage using this incoming message, to return from web service to MDS server. + /// + /// Response message to this message + public IWebServiceOutgoingMessage CreateResponseDataMessage() + { + return new OutgoingDataMessage + { + DestinationApplicationName = SourceApplicationName, + DestinationCommunicatorId = SourceCommunicatorId, + DestinationServerName = SourceServerName, + RepliedMessageId = MessageId, + TransmitRule = TransmitRule + }; + } + } + + /// + /// Implements IWebServiceOutgoingMessage to be used by MDS web service. + /// + private class OutgoingDataMessage : MDSDataTransferMessage, IWebServiceOutgoingMessage + { + //No data or method + } + + /// + /// Implements IWebServiceResponseMessage to be used by MDS web service. + /// + private class ResponseMessage : IWebServiceResponseMessage + { + public IWebServiceOperationResultMessage Result { get; set; } + + public IWebServiceOutgoingMessage Message { get; set; } + + public MDSDataTransferResponseMessage CreateDataTransferResponseMessage() + { + return new MDSDataTransferResponseMessage + { + Message = (MDSDataTransferMessage) Message, + Result = (MDSOperationResultMessage) Result + }; + } + } + + /// + /// Implements IWebServiceOperationResultMessage to be used by MDS web service. + /// + private class ResultMessage : MDSOperationResultMessage, IWebServiceOperationResultMessage + { + //No data or method + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Communication/Channels/CommunicationStateChangedEventArgs.cs b/src/MDSCommonLib/Communication/Channels/CommunicationStateChangedEventArgs.cs new file mode 100644 index 0000000..088038a --- /dev/null +++ b/src/MDSCommonLib/Communication/Channels/CommunicationStateChangedEventArgs.cs @@ -0,0 +1,42 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Communication.Channels +{ + /// + /// A delegate to create events for changing state of a communication channel. + /// Old state is passed with event arguments, new state can be get from communication channel object (sender). + /// + /// The communication channel object which raises event + /// Event arguments + public delegate void CommunicationStateChangedHandler(ICommunicationChannel sender, CommunicationStateChangedEventArgs e); + + /// + /// Stores informations about communication channel's state. + /// + public class CommunicationStateChangedEventArgs : EventArgs + { + /// + /// The state of the client before change. + /// + public CommunicationStates OldState { get; set; } + } +} diff --git a/src/MDSCommonLib/Communication/Channels/ICommunicationChannel.cs b/src/MDSCommonLib/Communication/Channels/ICommunicationChannel.cs new file mode 100644 index 0000000..4962e1d --- /dev/null +++ b/src/MDSCommonLib/Communication/Channels/ICommunicationChannel.cs @@ -0,0 +1,77 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Communication.Channels +{ + /// + /// All Communication channels implements this interface. + /// It is used by MDSClient and MDSController classes to communicate with MDS server. + /// + public interface ICommunicationChannel + { + /// + /// This event is raised when the state of the communication channel changes. + /// + event CommunicationStateChangedHandler StateChanged; + + /// + /// This event is raised when a MDSMessage object is received from MDS server. + /// + event MessageReceivedHandler MessageReceived; + + /// + /// Unique identifier for this communicator in connected MDS server. + /// This field is not set by communication channel, + /// it is set by another classes (MDSClient) that are using + /// communication channel. + /// + long ComminicatorId { get; set; } + + /// + /// Gets the state of communication channel. + /// + CommunicationStates State { get; } + + /// + /// Communication way for this channel. + /// This field is not set by communication channel, + /// it is set by another classes (MDSClient) that are using + /// communication channel. + /// + CommunicationWays CommunicationWay { get; set; } + + /// + /// Connects to MDS server. + /// + void Connect(); + + /// + /// Disconnects from MDS server. + /// + void Disconnect(); + + /// + /// Sends a MDSMessage to the MDS server + /// + /// Message to be sent + void SendMessage(MDSMessage message); + } +} diff --git a/src/MDSCommonLib/Communication/Channels/MessageReceivedEventArgs.cs b/src/MDSCommonLib/Communication/Channels/MessageReceivedEventArgs.cs new file mode 100644 index 0000000..20134ed --- /dev/null +++ b/src/MDSCommonLib/Communication/Channels/MessageReceivedEventArgs.cs @@ -0,0 +1,42 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; + +namespace MDS.Communication.Channels +{ + /// + /// A delegate to create events by Communication Channels, when a MDSMessage received from MDS server. + /// + /// The object which raises event + /// Event arguments + public delegate void MessageReceivedHandler(ICommunicationChannel sender, MessageReceivedEventArgs e); + + /// + /// Stores message informations. + /// + public class MessageReceivedEventArgs : EventArgs + { + /// + /// Received message from MDS server. + /// + public MDSMessage Message { get; set; } + } +} diff --git a/src/MDSCommonLib/Communication/Channels/TCPChannel.cs b/src/MDSCommonLib/Communication/Channels/TCPChannel.cs new file mode 100644 index 0000000..c9e215f --- /dev/null +++ b/src/MDSCommonLib/Communication/Channels/TCPChannel.cs @@ -0,0 +1,339 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication.Protocols; +using MDS.Exceptions; +using MDS.Communication.Messages; +using MDS.Serialization; + +namespace MDS.Communication.Channels +{ + /// + /// This class is used to connect to MDS server via TCP sockets. + /// + public class TCPChannel : ICommunicationChannel + { + #region Events + + /// + /// This event is raised when the state of the client changes. + /// + public event CommunicationStateChangedHandler StateChanged; + + /// + /// This event is raised when a MDSMessage received. + /// + public event MessageReceivedHandler MessageReceived; + + #endregion + + #region Public Properties + + /// + /// Unique identifier for this communicator in connected MDS server. + /// This field is not set by communication channel, + /// it is set by another classes (MDSClient) that are using + /// communication channel. + /// + public long ComminicatorId { get; set; } + + /// + /// Gets the connection state of communicator. + /// + public CommunicationStates State + { + get { return _state; } + } + private volatile CommunicationStates _state; + + /// + /// Communication way for this channel. + /// This field is not set by communication channel, + /// it is set by another classes (MDSClient) that are using + /// communication channel. + /// + public CommunicationWays CommunicationWay { get; set; } + + #endregion + + #region Private Fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// IP address of MDS server + /// + private readonly string _ipAddress; + + /// + /// TCP port of MDS server + /// + private readonly int _port; + + /// + /// The TCP socket to the remote application. + /// + private Socket _socket; + + /// + /// The main stream wraps socket to send/receive data. + /// + private NetworkStream _networkStream; + + /// + /// This object is used to send/receive messages as byte array. + /// + private readonly IMDSWireProtocol _wireProtocol; + + /// + /// The thread that listens incoming data. + /// + private Thread _thread; + + /// + /// Used to send only one message in a time by locking. + /// + private readonly object _sendLock; + + #endregion + + #region Constructors + + /// + /// Creates a new TCPChannel object. + /// + /// IP address of MDS server + /// TCP port of MDS server + public TCPChannel(string ipAddress, int port) + { + _ipAddress = ipAddress; + _port = port; + _state = CommunicationStates.Closed; + CommunicationWay = CommunicationWays.SendAndReceive; + _wireProtocol = new MDSDefaultWireProtocol(); + _sendLock = new object(); + } + + #endregion + + #region Public Methods + + /// + /// Connects to MDS server. + /// + public void Connect() + { + ChangeState(CommunicationStates.Connecting); + + try + { + var ip = IPAddress.Parse(_ipAddress); + + _socket = GeneralHelper.ConnectToServerWithTimeout(new IPEndPoint(ip, _port), 5000); //5 seconds + if (!_socket.Connected) + { + throw new MDSException("TCP connection can not be established."); + } + + _socket.NoDelay = true; + //Create stream object to read/write from/to socket. + _networkStream = new NetworkStream(_socket); + + //Create and start a new thread to communicate with MDS server + _thread = new Thread(DoCommunicateAsThread); + _thread.Start(); + + ChangeState(CommunicationStates.Connected); + } + catch (Exception) + { + ChangeState(CommunicationStates.Closed); + throw; + } + } + + /// + /// Disconnects from MDS server. + /// + public void Disconnect() + { + if (_socket == null) + { + return; + } + + ChangeState(CommunicationStates.Closing); + try + { + if (_socket.Connected) + { + _socket.Shutdown(SocketShutdown.Send); + _socket.Close(); + } + } + finally + { + _socket = null; + ChangeState(CommunicationStates.Closed); + } + } + + /// + /// Sends a MDSMessage to the MDS server + /// + /// Message to send + public void SendMessage(MDSMessage message) + { + lock (_sendLock) + { + if (State != CommunicationStates.Connected || !_socket.Connected) + { + throw new MDSException("Client's state is not connected. It can not send message."); + } + + SendMessageToSocket(message); + } + } + + #endregion + + #region Private methods + + /// + /// Entrance point of the thread. + /// This method run by thread to listen incoming data from communicator. + /// + private void DoCommunicateAsThread() + { + while (State == CommunicationStates.Connected || State == CommunicationStates.Connecting) + { + try + { + //Read a message from _networkStream (socket) and raise MessageReceived event + OnMessageReceived( + _wireProtocol.ReadMessage( + new MDSDefaultDeserializer(_networkStream) + )); + } + catch + { + //Stop listening on an error case + break; + } + } //while + + //if socket is steel connected, then close it + try + { + Disconnect(); + } + catch(Exception ex) + { + Logger.Debug(ex.Message, ex); + } + + _thread = null; + } + + /// + /// Sends MDSMessage object to the socket. + /// + /// Message to be sent + private void SendMessageToSocket(MDSMessage message) + { + //Create MemoryStream to write message to a byte array + var memoryStream = new MemoryStream(); + + //Write message + _wireProtocol.WriteMessage(new MDSDefaultSerializer(memoryStream), message); + + //Check the length of message data + if (memoryStream.Length > CommunicationConsts.MaxMessageSize) + { + throw new Exception("Message is too big to send."); + } + + //SendMessage message (contents of created memory stream) + var sendBuffer = memoryStream.ToArray(); + var length = sendBuffer.Length; + var totalSent = 0; + while (totalSent < length) + { + var sent = _socket.Send(sendBuffer, totalSent, length - totalSent, SocketFlags.None); + if (sent <= 0) + { + throw new Exception("Message can not be sent via TCP socket. Only " + totalSent + " bytes of " + length + " bytes are sent."); + } + + totalSent += sent; + } + } + + /// + /// Changes the state of the client and raises StateChanged event. + /// + /// New state + private void ChangeState(CommunicationStates newState) + { + var oldState = _state; + _state = newState; + if (StateChanged != null) + { + StateChanged(this, new CommunicationStateChangedEventArgs { OldState = oldState }); + } + } + + /// + /// When a MDSMessage received from MDS server, this method is called to raise MessageReceived event. + /// + /// Incoming message from server + private void OnMessageReceived(MDSMessage message) + { + if (MessageReceived == null) + { + return; + } + + try + { + MessageReceived(this, + new MessageReceivedEventArgs + { + Message = message + }); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MDSCommonLib/Communication/CommunicationConsts.cs b/src/MDSCommonLib/Communication/CommunicationConsts.cs new file mode 100644 index 0000000..5160c70 --- /dev/null +++ b/src/MDSCommonLib/Communication/CommunicationConsts.cs @@ -0,0 +1,42 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication +{ + /// + /// This class stores some consts used in MDS. + /// + public sealed class CommunicationConsts + { + /// + /// Default IP address to connect to MDS server. + /// + public const string DefaultIpAddress = "127.0.0.1"; + + /// + /// Default listening port of MDS server. + /// + public const int DefaultMDSPort = 10905; + + /// + /// Maximum message data length. + /// + public const uint MaxMessageSize = 52428800; //50 MegaBytes + } +} diff --git a/src/MDSCommonLib/Communication/CommunicationStates.cs b/src/MDSCommonLib/Communication/CommunicationStates.cs new file mode 100644 index 0000000..f47f88d --- /dev/null +++ b/src/MDSCommonLib/Communication/CommunicationStates.cs @@ -0,0 +1,47 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication +{ + /// + /// Represents states of a communication object. + /// + public enum CommunicationStates + { + /// + /// Connecting now.. + /// + Connecting, + + /// + /// Connection is established, communication can be made. + /// + Connected, + + /// + /// Closing connection.. + /// + Closing, + + /// + /// Connection is closed, so communication can not be made. + /// + Closed + } +} diff --git a/src/MDSCommonLib/Communication/CommunicationWays.cs b/src/MDSCommonLib/Communication/CommunicationWays.cs new file mode 100644 index 0000000..8b972bd --- /dev/null +++ b/src/MDSCommonLib/Communication/CommunicationWays.cs @@ -0,0 +1,38 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication +{ + /// + /// Communication ways. + /// A client application may just send messages from communication channel or it can send and receive messages. + /// + public enum CommunicationWays: byte + { + /// + /// Application can only send messages to MDS server. + /// + Send = 1, + + /// + /// Application can send and receive messages to/from MDS server. + /// + SendAndReceive = 2, + } +} diff --git a/src/MDSCommonLib/Communication/CommunicatorTypes.cs b/src/MDSCommonLib/Communication/CommunicatorTypes.cs new file mode 100644 index 0000000..2904275 --- /dev/null +++ b/src/MDSCommonLib/Communication/CommunicatorTypes.cs @@ -0,0 +1,50 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Communication +{ + /// + /// Represents types of communicatiors. + /// + [Serializable] + public enum CommunicatorTypes : byte + { + /// + /// An undefined remote application. + /// + Undefined = 0, + + /// + /// A MDS server. + /// + MdsServer = 1, + + /// + /// A client application. + /// + Application = 2, + + /// + /// A controller application. + /// + Controller = 3 + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/AddNewApplicationMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/AddNewApplicationMessage.cs new file mode 100644 index 0000000..cf586c4 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/AddNewApplicationMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent by MDS Manager to MDS Server to add a new Application to MDS. + /// + public class AddNewApplicationMessage : ControlMessage + { + /// + /// Gets MessageTypeId for AddNewApplicationMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdAddNewApplicationMessage; } + } + + /// + /// Name of the new application. + /// + public string ApplicationName { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ApplicationWebServiceInfo.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ApplicationWebServiceInfo.cs new file mode 100644 index 0000000..46cfb4a --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ApplicationWebServiceInfo.cs @@ -0,0 +1,52 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// Represents a web service communicator information for an application. + /// + public class ApplicationWebServiceInfo : IMDSSerializable + { + /// + /// Url of the web service. + /// + public string Url { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(Url); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public void Deserialize(IMDSDeserializer deserializer) + { + Url = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRefreshEventMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRefreshEventMessage.cs new file mode 100644 index 0000000..4bed081 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRefreshEventMessage.cs @@ -0,0 +1,69 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent to all connected MDS managers/controllers by MDS Server to inform about latest informations/state of a client application. + /// + public class ClientApplicationRefreshEventMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationListResponseMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdClientApplicationRefreshEventMessage; } + } + + /// + /// Name of the client application + /// + public string Name { get; set; } + + /// + /// Currently connected (online) communicator count. + /// + public int CommunicatorCount { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(Name); + serializer.WriteInt32(CommunicatorCount); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + Name = deserializer.ReadStringUTF8(); + CommunicatorCount = deserializer.ReadInt32(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRemovedEventMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRemovedEventMessage.cs new file mode 100644 index 0000000..204a17a --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ClientApplicationRemovedEventMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent to all connected MDS managers/controllers by MDS Server to inform when a client application is removed. + /// + public class ClientApplicationRemovedEventMessage : ControlMessage + { + /// + /// Gets MessageTypeId for ClientApplicationRemovedEventMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdClientApplicationRemovedEventMessage; } + } + + /// + /// Name of the new application. + /// + public string ApplicationName { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessage.cs new file mode 100644 index 0000000..98e4d2f --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessage.cs @@ -0,0 +1,53 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This is the base class for all messages that are being transmited between MDS server and Management (Controller) application. + /// + public abstract class ControlMessage : IMDSSerializable + { + /// + /// MessageTypeIf of message. + /// It is used to serialize/deserialize message. + /// + public abstract int MessageTypeId { get; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public virtual void Serialize(IMDSSerializer serializer) + { + //No data to serialize + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public virtual void Deserialize(IMDSDeserializer deserializer) + { + //No data to deserialize + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessageFactory.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessageFactory.cs new file mode 100644 index 0000000..bcdfe38 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ControlMessageFactory.cs @@ -0,0 +1,78 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Exceptions; + +namespace MDS.Communication.Messages.ControllerMessages +{ + public static class ControlMessageFactory + { + public const int MessageTypeIdGetApplicationListMessage = 1; + public const int MessageTypeIdGetApplicationListResponseMessage = 2; + public const int MessageTypeIdClientApplicationRefreshEventMessage = 3; + public const int MessageTypeIdAddNewApplicationMessage = 4; + public const int MessageTypeIdRemoveApplicationMessage = 5; + public const int MessageTypeIdRemoveApplicationResponseMessage = 6; + public const int MessageTypeIdClientApplicationRemovedEventMessage = 7; + public const int MessageTypeIdGetServerGraphMessage = 8; + public const int MessageTypeIdGetServerGraphResponseMessage = 9; + public const int MessageTypeIdUpdateServerGraphMessage = 10; + public const int MessageTypeIdOperationResultMessage = 11; + public const int MessageTypeIdGetApplicationWebServicesMessage = 12; + public const int MessageTypeIdGetApplicationWebServicesResponseMessage = 13; + public const int MessageTypeIdUpdateApplicationWebServicesMessage = 14; + + public static ControlMessage CreateMessageByTypeId(int messageTypeId) + { + switch (messageTypeId) + { + case MessageTypeIdGetApplicationListMessage: + return new GetApplicationListMessage(); + case MessageTypeIdGetApplicationListResponseMessage: + return new GetApplicationListResponseMessage(); + case MessageTypeIdClientApplicationRefreshEventMessage: + return new ClientApplicationRefreshEventMessage(); + case MessageTypeIdAddNewApplicationMessage: + return new AddNewApplicationMessage(); + case MessageTypeIdRemoveApplicationMessage: + return new RemoveApplicationMessage(); + case MessageTypeIdRemoveApplicationResponseMessage: + return new RemoveApplicationResponseMessage(); + case MessageTypeIdClientApplicationRemovedEventMessage: + return new ClientApplicationRemovedEventMessage(); + case MessageTypeIdGetServerGraphMessage: + return new GetServerGraphMessage(); + case MessageTypeIdGetServerGraphResponseMessage: + return new GetServerGraphResponseMessage(); + case MessageTypeIdUpdateServerGraphMessage: + return new UpdateServerGraphMessage(); + case MessageTypeIdOperationResultMessage: + return new OperationResultMessage(); + case MessageTypeIdGetApplicationWebServicesMessage: + return new GetApplicationWebServicesMessage(); + case MessageTypeIdGetApplicationWebServicesResponseMessage: + return new GetApplicationWebServicesResponseMessage(); + case MessageTypeIdUpdateApplicationWebServicesMessage: + return new UpdateApplicationWebServicesMessage(); + default: + throw new MDSException("Undefined ControlMessage MessageTypeId: " + messageTypeId); + } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListMessage.cs new file mode 100644 index 0000000..e633dad --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListMessage.cs @@ -0,0 +1,35 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent from MDS manager to MDS server to get list of all client applications. + /// + public class GetApplicationListMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationListMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetApplicationListMessage; } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListResponseMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListResponseMessage.cs new file mode 100644 index 0000000..08437b5 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationListResponseMessage.cs @@ -0,0 +1,98 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent from MDS server to MDS manager as a response to GetApplicationListMessage message. + /// + public class GetApplicationListResponseMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationListResponseMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetApplicationListResponseMessage; } + } + + /// + /// List of client applications. + /// + public ClientApplicationInfo[] ClientApplications { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteObjectArray(ClientApplications); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ClientApplications = deserializer.ReadObjectArray(() => new ClientApplicationInfo()); + } + + /// + /// This class is used to transfer simple information about a MDS Client Application. + /// + public class ClientApplicationInfo : IMDSSerializable + { + /// + /// Name of the client application + /// + public string Name { get; set; } + + /// + /// Currently connected (online) communicator count. + /// + public int CommunicatorCount { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(Name); + serializer.WriteInt32(CommunicatorCount); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public void Deserialize(IMDSDeserializer deserializer) + { + Name = deserializer.ReadStringUTF8(); + CommunicatorCount = deserializer.ReadInt32(); + } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesMessage.cs new file mode 100644 index 0000000..28ecd1c --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is used to get web services informations of an application from MDS server. + /// + public class GetApplicationWebServicesMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationWebServicesMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetApplicationWebServicesMessage; } + } + + /// + /// Name of the application to get web service information. + /// + public string ApplicationName { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesResponseMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesResponseMessage.cs new file mode 100644 index 0000000..eeaa265 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetApplicationWebServicesResponseMessage.cs @@ -0,0 +1,76 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is response to MDS Manager for GetApplicationWebServicesMessage. + /// + public class GetApplicationWebServicesResponseMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationWebServicesMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetApplicationWebServicesResponseMessage; } + } + + /// + /// True, if operation is success and no error occured. + /// + public bool Success { get; set; } + + /// + /// Detailed information about operation result. Error text, if any error occured. + /// + public string ResultText { get; set; } + + /// + /// Web service communicators of application. + /// + public ApplicationWebServiceInfo[] WebServices { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteBoolean(Success); + serializer.WriteStringUTF8(ResultText); + serializer.WriteObjectArray(WebServices); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + Success = deserializer.ReadBoolean(); + ResultText = deserializer.ReadStringUTF8(); + WebServices = deserializer.ReadObjectArray(() => new ApplicationWebServiceInfo()); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphMessage.cs new file mode 100644 index 0000000..75253e2 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphMessage.cs @@ -0,0 +1,35 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent from MDS manager to MDS server to get graph of MDS servers. + /// + public class GetServerGraphMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetServerGraphMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetServerGraphMessage; } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphResponseMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphResponseMessage.cs new file mode 100644 index 0000000..8ddcf42 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/GetServerGraphResponseMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent from MDS server to MDS manager as a response to GetServerGraphMessage message. + /// + public class GetServerGraphResponseMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetServerGraphMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdGetServerGraphResponseMessage; } + } + + /// + /// The ServerGraphInfo object that stores all server and graph informations. + /// + public ServerGraphInfo ServerGraph { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteObject(ServerGraph); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ServerGraph = deserializer.ReadObject(() => new ServerGraphInfo()); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/OperationResultMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/OperationResultMessage.cs new file mode 100644 index 0000000..42ce461 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/OperationResultMessage.cs @@ -0,0 +1,69 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is used to get an operation result message. + /// + public class OperationResultMessage : ControlMessage + { + /// + /// Gets MessageTypeId for OperationResultMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdOperationResultMessage; } + } + + /// + /// True, if operation is successfully executed. + /// + public bool Success { get; set; } + + /// + /// If Success = True then "Success.", else error message. + /// + public string ResultMessage { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteBoolean(Success); + serializer.WriteStringUTF8(ResultMessage); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + Success = deserializer.ReadBoolean(); + ResultMessage = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationMessage.cs new file mode 100644 index 0000000..e1ebe3b --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent by MDS Manager to MDS Server to remove a Application from MDS. + /// + public class RemoveApplicationMessage : ControlMessage + { + /// + /// Gets MessageTypeId for RemoveApplicationMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdRemoveApplicationMessage; } + } + + /// + /// Name of the removing application. + /// + public string ApplicationName { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationResponseMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationResponseMessage.cs new file mode 100644 index 0000000..488a83f --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/RemoveApplicationResponseMessage.cs @@ -0,0 +1,76 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent by MDS Server to MDS Manager as a response to a RemoveApplicationMessage. + /// + public class RemoveApplicationResponseMessage : ControlMessage + { + /// + /// Gets MessageTypeId for RemoveApplicationResponseMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdRemoveApplicationResponseMessage; } + } + + /// + /// Name of the new application. + /// + public string ApplicationName { get; set; } + + /// + /// True, if application is successfully removed. + /// + public bool Removed { get; set; } + + /// + /// If Removed = True then "Success", else error message. + /// + public string ResultMessage { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + serializer.WriteBoolean(Removed); + serializer.WriteStringUTF8(ResultMessage); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + Removed = deserializer.ReadBoolean(); + ResultMessage = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/ServerGraphInfo.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ServerGraphInfo.cs new file mode 100644 index 0000000..879864e --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/ServerGraphInfo.cs @@ -0,0 +1,117 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// Stores all MDS server's and server graph's informations. + /// + public class ServerGraphInfo : IMDSSerializable + { + /// + /// Reference to this server on graph (This server is also in Servers list). + /// + public string ThisServerName { get; set; } + + /// + /// All servers on graph. + /// + public ServerOnGraph[] Servers { get; set; } + + /// + /// Serializes this object. + /// + /// Serializer used to serialize objects + public void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(ThisServerName); + serializer.WriteObjectArray(Servers); + } + + /// + /// Deserializes this object. + /// + /// Deserializer used to deserialize objects + public void Deserialize(IMDSDeserializer deserializer) + { + ThisServerName = deserializer.ReadStringUTF8(); + Servers = deserializer.ReadObjectArray(() => new ServerOnGraph()); + } + + #region Sub classes + + public class ServerOnGraph : IMDSSerializable + { + /// + /// Name of this server. + /// + public string Name { get; set; } + + /// + /// IP address of this server. + /// + public string IpAddress { get; set; } + + /// + /// TCP Port number that is listened by this server. + /// + public int Port { get; set; } + + /// + /// List of adjacent servers of this server that are splitted by , or ; + /// + public string Adjacents { get; set; } + + /// + /// Location of server (Left (X) and Top (Y) properties in design area, seperated by comma (,)). + /// + public string Location { get; set; } + + /// + /// Serializes this object. + /// + /// Serializer used to serialize objects + public void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(Name); + serializer.WriteStringUTF8(IpAddress); + serializer.WriteInt32(Port); + serializer.WriteStringUTF8(Adjacents); + serializer.WriteStringUTF8(Location); + } + + /// + /// Deserializes this object. + /// + /// Deserializer used to deserialize objects + public void Deserialize(IMDSDeserializer deserializer) + { + Name = deserializer.ReadStringUTF8(); + IpAddress = deserializer.ReadStringUTF8(); + Port = deserializer.ReadInt32(); + Adjacents = deserializer.ReadStringUTF8(); + Location = deserializer.ReadStringUTF8(); + } + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateApplicationWebServicesMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateApplicationWebServicesMessage.cs new file mode 100644 index 0000000..90744d3 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateApplicationWebServicesMessage.cs @@ -0,0 +1,70 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + public class UpdateApplicationWebServicesMessage : ControlMessage + { + /// + /// Gets MessageTypeId for GetApplicationWebServicesMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdUpdateApplicationWebServicesMessage; } + } + + /// + /// Name of the application to get web service information. + /// + public string ApplicationName { get; set; } + + /// + /// Web service communicators of application. + /// + public ApplicationWebServiceInfo[] WebServices { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(ApplicationName); + serializer.WriteObjectArray(WebServices); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ApplicationName = deserializer.ReadStringUTF8(); + WebServices = deserializer.ReadObjectArray(() => new ApplicationWebServiceInfo()); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateServerGraphMessage.cs b/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateServerGraphMessage.cs new file mode 100644 index 0000000..d2027b9 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ControllerMessages/UpdateServerGraphMessage.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages.ControllerMessages +{ + /// + /// This message is sent from MDS manager to MDS server to update server graph of MDS. + /// + public class UpdateServerGraphMessage : ControlMessage + { + /// + /// Gets MessageTypeId for UpdateServerGraphMessage. + /// + public override int MessageTypeId + { + get { return ControlMessageFactory.MessageTypeIdUpdateServerGraphMessage; } + } + + /// + /// The ServerGraphInfo object that stores all server and graph informations. + /// + public ServerGraphInfo ServerGraph { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteObject(ServerGraph); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ServerGraph = deserializer.ReadObject(() => new ServerGraphInfo()); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSChangeCommunicationWayMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSChangeCommunicationWayMessage.cs new file mode 100644 index 0000000..541e58b --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSChangeCommunicationWayMessage.cs @@ -0,0 +1,65 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// This message is used to change Communication Way of a communicator while it is connected to the MDS server. + /// Thus, for example, a receiver may change it's communication way to only Send and it does not get messages + /// anymore but can send messages. + /// + public class MDSChangeCommunicationWayMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSChangeCommunicationWayMessage; } + } + + /// + /// New communication way. + /// + public CommunicationWays NewCommunicationWay { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteByte((byte) NewCommunicationWay); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + NewCommunicationWay = (CommunicationWays) deserializer.ReadByte(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSControllerMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSControllerMessage.cs new file mode 100644 index 0000000..418556c --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSControllerMessage.cs @@ -0,0 +1,64 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// This class represents a message that is being transmitted between MDS server and a Controller (MDS Manager). + /// + public class MDSControllerMessage : MDSMessage + { + /// + /// MessageTypeId for MDSControllerMessage. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSControllerMessage; } + } + + /// + /// MessageTypeId of ControllerMessage. + /// This field is used to deserialize MessageData. + /// All types defined in ControlMessageFactory class. + /// + public int ControllerMessageTypeId { get; set; } + + /// + /// Essential message data. + /// This is a serialized object of a class in MDS.Communication.Messages.ControllerMessages namespace. + /// + public byte[] MessageData { get; set; } + + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteInt32(ControllerMessageTypeId); + serializer.WriteByteArray(MessageData); + } + + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + ControllerMessageTypeId = deserializer.ReadInt32(); + MessageData = deserializer.ReadByteArray(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSDataTransferMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSDataTransferMessage.cs new file mode 100644 index 0000000..350f812 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSDataTransferMessage.cs @@ -0,0 +1,143 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// Represents a Data Transfer message. + /// Used to transfer real message data between client applications. + /// + public class MDSDataTransferMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSDataTransferMessage; } + } + + /// + /// Name of the first source server of the message. + /// + public string SourceServerName { get; set; } + + /// + /// Name of the first source application of the message. + /// If the message is created by an MDS (source of message is not an application) + /// then SourceApplicationName must be set to null. + /// + public string SourceApplicationName { get; set; } + + /// + /// The source communication channel's (Communicator's) Id. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicates a spesific communicator. + /// This field is set by MDS automatically. + /// + public long SourceCommunicatorId { get; set; } + + /// + /// Name of the final destination server of the message. + /// + public string DestinationServerName { get; set; } + + /// + /// Name of the final destination application of the message. + /// If the message is sent to an MDS (destination of message is not an application), + /// then DestinationApplicationName must be set to null. + /// + public string DestinationApplicationName { get; set; } + + /// + /// Destination communication channel's (Communicator's) Id. + /// This field is used by MDS to deliver message to a spesific communicator. + /// When more than one communicator of an application is connected same MDS server + /// at the same time, this field may be used to indicate a spesific communicator as receiver of message. + /// If it is set to 0 (zero), message may be delivered to any connected communicator. + /// If there is no communicator with DestinationCommunicatorId, message can not be delivered, so, + /// this field can only be used to send non-persistent messages. + /// + public long DestinationCommunicatorId { get; set; } + + /// + /// Passed servers of message until now, includes source and destination servers. + /// + public ServerTransmitReport[] PassedServers { get; set; } + + /// + /// Essential application message data to transfer. + /// + public byte[] MessageData { get; set; } + + /// + /// Transmit rule of message. + /// This is important because it determines persistence and transmit time of message. + /// Default: StoreAndForward. + /// + public MessageTransmitRules TransmitRule { get; set; } + + /// + /// Creates a new MDSDataTransferMessage object. + /// + public MDSDataTransferMessage() + { + TransmitRule = MessageTransmitRules.StoreAndForward; //Default TransmitRule value + } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteStringUTF8(SourceServerName); + serializer.WriteStringUTF8(SourceApplicationName); + serializer.WriteInt64(SourceCommunicatorId); + serializer.WriteStringUTF8(DestinationServerName); + serializer.WriteStringUTF8(DestinationApplicationName); + serializer.WriteInt64(DestinationCommunicatorId); + serializer.WriteObjectArray(PassedServers); + serializer.WriteByteArray(MessageData); + serializer.WriteByte((byte) TransmitRule); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + SourceServerName = deserializer.ReadStringUTF8(); + SourceApplicationName = deserializer.ReadStringUTF8(); + SourceCommunicatorId = deserializer.ReadInt64(); + DestinationServerName = deserializer.ReadStringUTF8(); + DestinationApplicationName = deserializer.ReadStringUTF8(); + DestinationCommunicatorId = deserializer.ReadInt64(); + PassedServers = deserializer.ReadObjectArray(() => new ServerTransmitReport()); + MessageData = deserializer.ReadByteArray(); + TransmitRule = (MessageTransmitRules) deserializer.ReadByte(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSDataTransferResponseMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSDataTransferResponseMessage.cs new file mode 100644 index 0000000..950148a --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSDataTransferResponseMessage.cs @@ -0,0 +1,71 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// This message is used to acknowledge/reject a message and to send a MDSDataTransferMessage in same message object. + /// It is used in web services. + /// + public class MDSDataTransferResponseMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSDataTransferResponseMessage; } + } + + /// + /// This field is used to acknowledge/reject to an incoming message. + /// + public MDSOperationResultMessage Result { get; set; } + + /// + /// This field is used to send a new message. + /// + public MDSDataTransferMessage Message { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteObject(Result); + serializer.WriteObject(Message); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + Result = deserializer.ReadObject(() => new MDSOperationResultMessage()); + Message = deserializer.ReadObject(() => new MDSDataTransferMessage()); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSMessage.cs new file mode 100644 index 0000000..92157e3 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSMessage.cs @@ -0,0 +1,77 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// Abstract class of all message classes. + /// All messages transmiting on MDS must be derrived from this class. + /// + public abstract class MDSMessage : IMDSSerializable + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public abstract int MessageTypeId { get; } + + /// + /// Unique ID for this message. + /// Thiss will be a GUID if it is not set. + /// + public string MessageId { get; set; } + + /// + /// If this message is a reply for another message then RepliedMessageId contains first message's MessageId + /// else RepliedMessageId is null default. + /// + public string RepliedMessageId { get; set; } + + /// + /// Constructor. + /// + protected MDSMessage() + { + MessageId = Guid.NewGuid().ToString(); + } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public virtual void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(MessageId); + serializer.WriteStringUTF8(RepliedMessageId); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public virtual void Deserialize(IMDSDeserializer deserializer) + { + MessageId = deserializer.ReadStringUTF8(); + RepliedMessageId = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSMessageFactory.cs b/src/MDSCommonLib/Communication/Messages/MDSMessageFactory.cs new file mode 100644 index 0000000..f3779c1 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSMessageFactory.cs @@ -0,0 +1,57 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Exceptions; + +namespace MDS.Communication.Messages +{ + public static class MDSMessageFactory + { + public const int MessageTypeIdMDSDataTransferMessage = 1; + public const int MessageTypeIdMDSOperationResultMessage = 2; + public const int MessageTypeIdMDSPingMessage = 3; + public const int MessageTypeIdMDSRegisterMessage = 4; + public const int MessageTypeIdMDSChangeCommunicationWayMessage = 5; + public const int MessageTypeIdMDSControllerMessage = 6; + public const int MessageTypeIdMDSDataTransferResponseMessage = 7; + + public static MDSMessage CreateMessageByTypeId(int messageTypeId) + { + switch (messageTypeId) + { + case MessageTypeIdMDSDataTransferMessage: + return new MDSDataTransferMessage(); + case MessageTypeIdMDSOperationResultMessage: + return new MDSOperationResultMessage(); + case MessageTypeIdMDSPingMessage: + return new MDSPingMessage(); + case MessageTypeIdMDSRegisterMessage: + return new MDSRegisterMessage(); + case MessageTypeIdMDSChangeCommunicationWayMessage: + return new MDSChangeCommunicationWayMessage(); + case MessageTypeIdMDSControllerMessage: + return new MDSControllerMessage(); + case MessageTypeIdMDSDataTransferResponseMessage: + return new MDSDataTransferResponseMessage(); + default: + throw new MDSException("Unknown MessageTypeId: " + messageTypeId); + } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSOperationResultMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSOperationResultMessage.cs new file mode 100644 index 0000000..6a77c1f --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSOperationResultMessage.cs @@ -0,0 +1,73 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// This message is sent to clients as a response to an operation. + /// It is generally used to send an ACK/Reject message for a message + /// or a response to register message. + /// + public class MDSOperationResultMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSOperationResultMessage; } + } + + /// + /// Operation result. + /// True, if operation is successful. + /// + public bool Success { get; set; } + + /// + /// A text that may be used as a description for result of operation. + /// + public string ResultText { get; set; } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteBoolean(Success); + serializer.WriteStringUTF8(ResultText); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + Success = deserializer.ReadBoolean(); + ResultText = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSPingMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSPingMessage.cs new file mode 100644 index 0000000..3ac100f --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSPingMessage.cs @@ -0,0 +1,40 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication.Messages +{ + /// + /// This class is used to send Ping messages to check if remote application is connected and working. + /// MDS Servers send Ping messages to other MDS servers and gets response. + /// Client applications send Ping messages to MDS servers and gets response. + /// If there is no Ping message from a remote application for a while, connection is closed and + /// reconnected if needed. + /// + public class MDSPingMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSPingMessage; } + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MDSRegisterMessage.cs b/src/MDSCommonLib/Communication/Messages/MDSRegisterMessage.cs new file mode 100644 index 0000000..0a23f26 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MDSRegisterMessage.cs @@ -0,0 +1,95 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// Register Message. A MDSRegisterMessage object is used to register a MDS server as an Application or MDS server. + /// + public class MDSRegisterMessage : MDSMessage + { + /// + /// MessageTypeId of message. + /// It is used to serialize/deserialize message. + /// + public override int MessageTypeId + { + get { return MDSMessageFactory.MessageTypeIdMDSRegisterMessage; } + } + + /// + /// Communicator type (MDS server, Application or Controller). + /// + public CommunicatorTypes CommunicatorType { get; set; } + + /// + /// Communication way for this communicator (SEND, RECEIVE or BOTH) + /// + public CommunicationWays CommunicationWay { get; set; } + + /// + /// Name of the communicator. + /// If CommunicatorType is a MDS, than this is server's name, + /// if CommunicatorType is an Application, than this is application's name, + /// if CommunicatorType is a Controller, than this is an arbitrary string represents controller. + /// + public string Name { get; set; } + + /// + /// Password to connect to MDS associated with Name and CommunicatorType. + /// + public string Password { get; set; } + + /// + /// Creates a new MDSRegisterMessage object. + /// + public MDSRegisterMessage() + { + CommunicationWay = CommunicationWays.Send; + } + + /// + /// Serializes this message. + /// + /// Serializer used to serialize objects + public override void Serialize(IMDSSerializer serializer) + { + base.Serialize(serializer); + serializer.WriteByte((byte)CommunicatorType); + serializer.WriteByte((byte)CommunicationWay); + serializer.WriteStringUTF8(Name); + serializer.WriteStringUTF8(Password); + } + + /// + /// Deserializes this message. + /// + /// Deserializer used to deserialize objects + public override void Deserialize(IMDSDeserializer deserializer) + { + base.Deserialize(deserializer); + CommunicatorType = (CommunicatorTypes) deserializer.ReadByte(); + CommunicationWay = (CommunicationWays) deserializer.ReadByte(); + Name = deserializer.ReadStringUTF8(); + Password = deserializer.ReadStringUTF8(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Messages/MessageTransmitRules.cs b/src/MDSCommonLib/Communication/Messages/MessageTransmitRules.cs new file mode 100644 index 0000000..e86507c --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/MessageTransmitRules.cs @@ -0,0 +1,58 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication.Messages +{ + /// + /// Message transmit rules. + /// All messages are persistent except 'DirectlySend'. + /// If a server doesn't stores message and transmiting it directly, + /// it transmits this message before than a stored (persistent) message. + /// + public enum MessageTransmitRules : byte + { + /// + /// Not persistent message. + /// Message may be lost in an error. + /// Message is not stored on any server. + /// Message is not guarantied to be delivered. + /// This rule may be used if both of source and destination applications must be run at the same time. + /// If no exception received while sending message, + /// that means message delivered to and acknowledged by destination application correctly. + /// This rule blocks sender application until destination application sends ACK for message. + /// + DirectlySend = 0, + + /// + /// Persistent Message. + /// Message can not be lost and it is being stored in all passing servers. + /// Message is guarantied to be delivered and it will be delivered as ordered (FIFO). + /// This is the slowest but most reliable rule. + /// This rule blocks sender application until source (first) MDS server stores message. + /// + StoreAndForward, + + /// + /// Non-persistent message. + /// Message will be lost if MDS server which has message shuts down. + /// Message is not guarantied to be delivered. + /// + NonPersistent + } +} diff --git a/src/MDSCommonLib/Communication/Messages/ServerTransmitReport.cs b/src/MDSCommonLib/Communication/Messages/ServerTransmitReport.cs new file mode 100644 index 0000000..f71df99 --- /dev/null +++ b/src/MDSCommonLib/Communication/Messages/ServerTransmitReport.cs @@ -0,0 +1,76 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Serialization; + +namespace MDS.Communication.Messages +{ + /// + /// This class is used to store transmit informations of a message throught a server. + /// + public class ServerTransmitReport : IMDSSerializable + { + /// + /// Name of the server. + /// + public string ServerName { get; set; } + + /// + /// Message arriving time to server. + /// + public DateTime ArrivingTime { get; set; } + + /// + /// Message leaving time from server. + /// + public DateTime LeavingTime { get; set; } + + /// + /// Creates a new ServerTransmitReport. + /// + public ServerTransmitReport() + { + ArrivingTime = DateTime.MinValue; + LeavingTime = DateTime.MinValue; + } + + /// + /// Serializes this object. + /// + /// Serializer used to serialize objects + public void Serialize(IMDSSerializer serializer) + { + serializer.WriteStringUTF8(ServerName); + serializer.WriteDateTime(ArrivingTime); + serializer.WriteDateTime(LeavingTime); + } + + /// + /// Deserializes this object. + /// + /// Deserializer used to deserialize objects + public void Deserialize(IMDSDeserializer deserializer) + { + ServerName = deserializer.ReadStringUTF8(); + ArrivingTime = deserializer.ReadDateTime(); + LeavingTime = deserializer.ReadDateTime(); + } + } +} diff --git a/src/MDSCommonLib/Communication/Protocols/IMDSWireProtocol.cs b/src/MDSCommonLib/Communication/Protocols/IMDSWireProtocol.cs new file mode 100644 index 0000000..58fa5c8 --- /dev/null +++ b/src/MDSCommonLib/Communication/Protocols/IMDSWireProtocol.cs @@ -0,0 +1,44 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; +using MDS.Serialization; + +namespace MDS.Communication.Protocols +{ + /// + /// This interface is used to Write/Read messages according to a Wire/Communication Protocol. + /// + public interface IMDSWireProtocol + { + /// + /// Serializes and writes a MDSMessage according to the protocol rules. + /// + /// Serializer to serialize message + /// Message to be serialized + void WriteMessage(IMDSSerializer serializer, MDSMessage message); + + /// + /// Reads and constructs a MDSMessage according to the protocol rules. + /// + /// Deserializer to read message + /// MDSMessage object that is read + MDSMessage ReadMessage(IMDSDeserializer deserializer); + } +} diff --git a/src/MDSCommonLib/Communication/Protocols/MDSDefaultWireProtocol.cs b/src/MDSCommonLib/Communication/Protocols/MDSDefaultWireProtocol.cs new file mode 100644 index 0000000..dd05bcf --- /dev/null +++ b/src/MDSCommonLib/Communication/Protocols/MDSDefaultWireProtocol.cs @@ -0,0 +1,81 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Serialization; + +namespace MDS.Communication.Protocols +{ + /// + /// This class is the Default Protocol that is used by MDS to communicate with other applications. + /// A message frame is sent and received by MDSDefaultWireProtocol: + /// + /// - Protocol type: 4 bytes unsigned integer. + /// Must be MDSDefaultProtocolType for MDSDefaultWireProtocol. + /// - Message type: 4 bytes integer. + /// Must be defined in MDSMessageFactory class. + /// - Serialized bytes of a MDSMessage object. + /// + public class MDSDefaultWireProtocol : IMDSWireProtocol + { + /// + /// Specific number that a message must start with. + /// + public const uint MDSDefaultProtocolType = 19180685; + + /// + /// Serializes and writes a MDSMessage according to the protocol rules. + /// + /// Serializer to serialize message + /// Message to be serialized + public void WriteMessage(IMDSSerializer serializer, MDSMessage message) + { + //Write protocol type + serializer.WriteUInt32(MDSDefaultProtocolType); + + //Write the message type + serializer.WriteInt32(message.MessageTypeId); + + //Write message + serializer.WriteObject(message); + } + + /// + /// Reads and constructs a MDSMessage according to the protocol rules. + /// + /// Deserializer to read message + /// MDSMessage object that is read + public MDSMessage ReadMessage(IMDSDeserializer deserializer) + { + //Read protocol type + var protocolType = deserializer.ReadUInt32(); + if (protocolType != MDSDefaultProtocolType) + { + throw new MDSException("Wrong protocol type: " + protocolType + "."); + } + + //Read message type + var messageTypeId = deserializer.ReadInt32(); + + //Read and return message + return deserializer.ReadObject(() => MDSMessageFactory.CreateMessageByTypeId(messageTypeId)); + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSDatabaseException.cs b/src/MDSCommonLib/Exceptions/MDSDatabaseException.cs new file mode 100644 index 0000000..31779b9 --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSDatabaseException.cs @@ -0,0 +1,64 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Exceptions +{ + /// + /// Represents a Database exception. + /// + [Serializable] + public class MDSDatabaseException : MDSException + { + /// + /// Executed query text + /// + public string QueryText { set; get; } + + /// + /// Contstructor. + /// + public MDSDatabaseException() + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSDatabaseException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSDatabaseException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSException.cs b/src/MDSCommonLib/Exceptions/MDSException.cs new file mode 100644 index 0000000..5ef9c5c --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSException.cs @@ -0,0 +1,70 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Runtime.Serialization; + +namespace MDS.Exceptions +{ + /// + /// Represents a MDS Exception. + /// This is the base class for exceptions that are thrown by MDS system. + /// + [Serializable] + public class MDSException : Exception + { + /// + /// Contstructor. + /// + public MDSException() + { + + } + + /// + /// Contstructor. + /// + public MDSException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSNoCommunicatorException.cs b/src/MDSCommonLib/Exceptions/MDSNoCommunicatorException.cs new file mode 100644 index 0000000..ecda544 --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSNoCommunicatorException.cs @@ -0,0 +1,59 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Exceptions +{ + /// + /// This exception is thrown when there is not a communicator of a remote application. + /// + [Serializable] + public class MDSNoCommunicatorException : MDSException + { + /// + /// Contstructor. + /// + public MDSNoCommunicatorException() + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSNoCommunicatorException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSNoCommunicatorException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSRemoteException.cs b/src/MDSCommonLib/Exceptions/MDSRemoteException.cs new file mode 100644 index 0000000..c27094b --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSRemoteException.cs @@ -0,0 +1,70 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Runtime.Serialization; + +namespace MDS.Exceptions +{ + /// + /// Represents a MDS Remote Exception. + /// This exception is used to send an exception from an application to another application. + /// + [Serializable] + public class MDSRemoteException : MDSException + { + /// + /// Contstructor. + /// + public MDSRemoteException() + { + + } + + /// + /// Contstructor. + /// + public MDSRemoteException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSRemoteException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSRemoteException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSSerializationException.cs b/src/MDSCommonLib/Exceptions/MDSSerializationException.cs new file mode 100644 index 0000000..afcbab0 --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSSerializationException.cs @@ -0,0 +1,59 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Exceptions +{ + /// + /// Represents an Serialization / Deserialization exception. + /// + [Serializable] + public class MDSSerializationException : MDSException + { + /// + /// Contstructor. + /// + public MDSSerializationException() + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSSerializationException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSSerializationException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/Exceptions/MDSTimeoutException.cs b/src/MDSCommonLib/Exceptions/MDSTimeoutException.cs new file mode 100644 index 0000000..92d6bb6 --- /dev/null +++ b/src/MDSCommonLib/Exceptions/MDSTimeoutException.cs @@ -0,0 +1,59 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Exceptions +{ + /// + /// Represents an Timeout exception. + /// + [Serializable] + public class MDSTimeoutException : MDSException + { + /// + /// Contstructor. + /// + public MDSTimeoutException() + { + + } + + /// + /// Contstructor. + /// + /// Exception message + public MDSTimeoutException(string message) + : base(message) + { + + } + + /// + /// Contstructor. + /// + /// Exception message + /// Inner exception + public MDSTimeoutException(string message, Exception innerException) + : base(message, innerException) + { + + } + } +} diff --git a/src/MDSCommonLib/GUI/MDSGuiHelper.cs b/src/MDSCommonLib/GUI/MDSGuiHelper.cs new file mode 100644 index 0000000..4d1abd3 --- /dev/null +++ b/src/MDSCommonLib/GUI/MDSGuiHelper.cs @@ -0,0 +1,104 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Windows.Forms; + +namespace MDS.GUI +{ + /// + /// This class is created to make easy common GUI tasks. + /// + public static class MDSGuiHelper + { + #region MessageBoxes + + /// + /// Show a message box that show an error. + /// + /// Message to show + public static void ShowErrorMessage(string message) + { + ShowErrorMessage(message, "Error!"); + } + + /// + /// Show a message box that show an error. + /// + /// Message to show + /// Caption of message box + public static void ShowErrorMessage(string message, string caption) + { + MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + /// + /// Show a message box that show an warning. + /// + /// Message to show + public static void ShowWarningMessage(string message) + { + ShowWarningMessage(message, "Warning!"); + } + + /// + /// Show a message box that show an warning. + /// + /// Message to show + /// Caption of message box + public static void ShowWarningMessage(string message, string caption) + { + MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + /// + /// Shows a messagebox to ask a question to user. + /// + /// Message to show + /// Caption of message box + /// User's choice + public static DialogResult ShowQuestionDialog(string message, string caption) + { + return MessageBox.Show(message, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question); + } + + /// + /// Shows a messagebox to ask a question to user. + /// + /// Message to show + /// Caption of message box + /// Default selected button + /// User's choice + public static DialogResult ShowQuestionDialog(string message, string caption, MessageBoxDefaultButton defaultButton) + { + return MessageBox.Show(message, caption, MessageBoxButtons.YesNo, MessageBoxIcon.Question, defaultButton); + } + + /// + /// Shows a messagebox that shows an information. + /// + /// Message to show + /// Caption of message box + public static void ShowInfoDialog(string message, string caption) + { + MessageBox.Show(message, caption, MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + #endregion + } +} diff --git a/src/MDSCommonLib/GeneralHelper.cs b/src/MDSCommonLib/GeneralHelper.cs new file mode 100644 index 0000000..1fff788 --- /dev/null +++ b/src/MDSCommonLib/GeneralHelper.cs @@ -0,0 +1,107 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; +using MDS.Exceptions; + +namespace MDS +{ + /// + /// This class is used to perform common tasks that is used in both client and server side. + /// + public static class GeneralHelper + { + /// + /// This code is used to connect to a TCP socket with timeout option. + /// + /// IP endpoint of remote server + /// Timeout to wait until connect + /// Socket object connected to server + public static Socket ConnectToServerWithTimeout(EndPoint endPoint, int timeoutMs) + { + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + try + { + socket.Blocking = false; + socket.Connect(endPoint); + socket.Blocking = true; + return socket; + } + catch (SocketException socketException) + { + if (socketException.ErrorCode != 10035) + { + socket.Close(); + throw; + } + + if (!socket.Poll(timeoutMs * 1000, SelectMode.SelectWrite)) + { + socket.Close(); + throw new MDSException("The host failed to connect. Timeout occured."); + } + + socket.Blocking = true; + return socket; + } + } + + public static byte[] SerializeObject(object obj) + { + var memoryStream = new MemoryStream(); + new BinaryFormatter().Serialize(memoryStream, obj); + return memoryStream.ToArray(); + } + + public static object DeserializeObject(byte[] bytesOfObject) + { + return new BinaryFormatter().Deserialize(new MemoryStream(bytesOfObject) { Position = 0 }); + } + + /// + /// Gets the current directory of executing assembly. + /// + /// Directory path + public static string GetCurrentDirectory() + { + string directory; + try + { + directory = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory.FullName; + } + catch (Exception) + { + directory = Directory.GetCurrentDirectory(); + } + + var directorySeparatorChar = Path.DirectorySeparatorChar.ToString(); + if (!directory.EndsWith(directorySeparatorChar)) + { + directory += directorySeparatorChar; + } + + return directory; + } + } +} diff --git a/src/MDSCommonLib/Liscense.txt b/src/MDSCommonLib/Liscense.txt new file mode 100644 index 0000000..83a488d --- /dev/null +++ b/src/MDSCommonLib/Liscense.txt @@ -0,0 +1,16 @@ +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/MDSCommonLib/MDSCommonLib.csproj b/src/MDSCommonLib/MDSCommonLib.csproj new file mode 100644 index 0000000..641c4c4 --- /dev/null +++ b/src/MDSCommonLib/MDSCommonLib.csproj @@ -0,0 +1,155 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {055149D1-A267-4E2E-B8AE-EA7848A45701} + Library + Properties + MDS + MDSCommonLib + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\MDSCommonLib.XML + + + + False + ..\Dependencies\Libraries\log4net.dll + + + + 3.5 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MDSCommonLib/Management/ControlMessageReceivedEventArgs.cs b/src/MDSCommonLib/Management/ControlMessageReceivedEventArgs.cs new file mode 100644 index 0000000..3f56d2f --- /dev/null +++ b/src/MDSCommonLib/Management/ControlMessageReceivedEventArgs.cs @@ -0,0 +1,51 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages.ControllerMessages; + +namespace MDS.Management +{ + /// + /// A delegate to create events when a control message received from MDS server. + /// + /// The object which raises event + /// Event arguments + public delegate void ControlMessageReceivedHandler(object sender, ControlMessageReceivedEventArgs e); + + /// + /// Stores message informations. + /// + public class ControlMessageReceivedEventArgs : EventArgs + { + /// + /// Received message from MDS server. + /// + public ControlMessage Message { get; set; } + + /// + /// Creates a ControlMessageReceivedEventArgs object. + /// + /// Received message from MDS server + public ControlMessageReceivedEventArgs(ControlMessage message) + { + Message = message; + } + } +} diff --git a/src/MDSCommonLib/Management/MDSController.cs b/src/MDSCommonLib/Management/MDSController.cs new file mode 100644 index 0000000..c2c7684 --- /dev/null +++ b/src/MDSCommonLib/Management/MDSController.cs @@ -0,0 +1,513 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication; +using MDS.Exceptions; +using MDS.Serialization; +using MDS.Communication.Channels; +using MDS.Communication.Messages; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Threading; + +namespace MDS.Management +{ + /// + /// This class is used to connect to and communicate with MDS server from MDS Manager (Controller). + /// + public class MDSController :IDisposable + { + #region Events + + /// + /// This event is raised when a data transfer message received from MDS server. + /// + public event ControlMessageReceivedHandler ControlMessageReceived; + + #endregion + + #region Public properties + + /// + /// Gets sets Reconnecting option on any error case. + /// If this is true, controller application attempts to reconnec to MDS server until it is connected, + /// MDSController doesn't throw exceptions while connecting. + /// Default value: True. + /// + public bool ReConnectServerOnError { get; set; } + + #endregion + + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Communication channel that is used to communicate with MDS server. + /// + private readonly ICommunicationChannel _communicationChannel; + + /// + /// This queue is used to queue MDSMessage objects received from MDS server and process them sequentially. + /// + private readonly QueueProcessorThread _incomingMessageQueue; + + /// + /// This collection is used to send message and get response in SendMessageAndGetResponse method. + /// SendMessageAndGetResponse method must wait until response received. It waits using this collection. + /// Key: Message ID to wait response. + /// Value: ManualResetEvent to wait thread until response received. + /// + private readonly SortedList _waitingMessages; + + /// + /// Time of last message received from MDS server. + /// + public DateTime LastIncomingMessageTime { get; set; } + + /// + /// Time of last message sent to MDS server. + /// + public DateTime LastOutgoingMessageTime { get; set; } + + /// + /// This timer is used to reconnect to MDS server if it is disconnected. + /// + private readonly Timer _reconnectTimer; + + /// + /// Used to Start/Stop MDSController, and indicates the state. + /// + private volatile bool _running; + + #endregion + + #region Constructors + + /// + /// Creates a new MDSClient object. + /// + /// Ip address of the MDS server + /// Listening TCP Port of MDS server + public MDSController(string ipAddress, int port) + { + ReConnectServerOnError = true; + + _reconnectTimer = new Timer(ReconnectTimer_Tick, null, Timeout.Infinite, Timeout.Infinite); + _waitingMessages = new SortedList(); + + _incomingMessageQueue = new QueueProcessorThread(); + _incomingMessageQueue.ProcessItem += IncomingMessageQueue_ProcessItem; + + _communicationChannel = new TCPChannel(ipAddress, port); + _communicationChannel.MessageReceived += CommunicationChannel_MessageReceived; + _communicationChannel.StateChanged += CommunicationChannel_StateChanged; + + LastIncomingMessageTime = DateTime.MinValue; + LastOutgoingMessageTime = DateTime.MinValue; + } + + #endregion + + #region Public methods + + #region Connect / Disconnect / Dispose methods + + /// + /// Connects to MDS server. + /// + public void Connect() + { + _incomingMessageQueue.Start(); + + try + { + _running = true; + ConnectAndRegister(); + } + catch (Exception) + { + if (!ReConnectServerOnError) + { + _running = false; + throw; + } + } + + _reconnectTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + + /// + /// Disconnects from MDS server. + /// + public void Disconnect() + { + lock (_reconnectTimer) + { + _running = false; + _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + CloseCommunicationChannel(); + _incomingMessageQueue.Stop(true); + } + + + /// + /// Disposes MDSController object. + /// It also disconnects from server if it is connected. + /// + public void Dispose() + { + if (_communicationChannel != null) + { + Disconnect(); + } + } + + #endregion + + #region Message sending methods + + /// + /// Sends a ControlMessage to MDS server. + /// + /// Message to send + public void SendMessage(ControlMessage message) + { + SendMessageInternal(new MDSControllerMessage + { + MessageData = MDSSerializationHelper.SerializeToByteArray(message), + ControllerMessageTypeId = message.MessageTypeId + }); + } + + /// + /// Sends a ControlMessage to MDS server and gets it's response message. + /// + /// Message to send + /// Response message from server + public ControlMessage SendMessageAndGetResponse(ControlMessage message) + { + //Create a WaitingMessage to wait and get response message and add it to waiting messages + var outgoingMessage = new MDSControllerMessage + { + MessageData = MDSSerializationHelper.SerializeToByteArray(message), + ControllerMessageTypeId = message.MessageTypeId + }; + var waitingMessage = new WaitingMessage(); + lock (_waitingMessages) + { + _waitingMessages[outgoingMessage.MessageId] = waitingMessage; + } + + try + { + //Send message to the server + SendMessageInternal(outgoingMessage); + + //Wait until thread is signalled by another thread to get response (Signalled by CommunicationChannel_MessageReceived method) + waitingMessage.WaitEvent.WaitOne(TimeSpan.FromSeconds(90)); + + //Check if response received or timeout occured + if(waitingMessage.ResponseMessage == null) + { + throw new MDSException("Timeout occured. Response message did not received."); + } + + return DeserializeControlMessage(waitingMessage.ResponseMessage); + } + finally + { + //Remove message from waiting messages + lock (_waitingMessages) + { + if (_waitingMessages.ContainsKey(outgoingMessage.MessageId)) + { + _waitingMessages.Remove(outgoingMessage.MessageId); + } + } + } + } + + #endregion + + #endregion + + #region Private methods + + /// + /// Connects and registers to MDS server. + /// + private void ConnectAndRegister() + { + _communicationChannel.Connect(); + try + { + SendMessageInternal( + new MDSRegisterMessage + { + CommunicationWay = CommunicationWays.SendAndReceive, + CommunicatorType = CommunicatorTypes.Controller, + Name = "MDSController", + Password = "" + }); + } + catch (MDSTimeoutException) + { + CloseCommunicationChannel(); + throw new MDSTimeoutException("Timeout occured. Can not registered to MDS server."); + } + } + + /// + /// Sends a MDSMessage object to MDS server. + /// + /// + private void SendMessageInternal(MDSMessage message) + { + try + { + _communicationChannel.SendMessage(message); + LastOutgoingMessageTime = DateTime.Now; + } + catch (Exception) + { + CloseCommunicationChannel(); + throw; + } + } + + /// + /// This event handles incoming messages from communication channel. + /// + /// Communication channel that received message + /// Event arguments + private void CommunicationChannel_MessageReceived(ICommunicationChannel sender, MessageReceivedEventArgs e) + { + LastIncomingMessageTime = DateTime.Now; + + if ((e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSControllerMessage) && (!string.IsNullOrEmpty(e.Message.RepliedMessageId))) + { + //Find and send signal/pulse to waiting thread for this message + WaitingMessage waitingMessage = null; + lock (_waitingMessages) + { + if (_waitingMessages.ContainsKey(e.Message.RepliedMessageId)) + { + waitingMessage = _waitingMessages[e.Message.RepliedMessageId]; + } + } + + if (waitingMessage != null) + { + waitingMessage.ResponseMessage = e.Message as MDSControllerMessage; + waitingMessage.WaitEvent.Set(); + return; + } + } + + //Add message to queue to process in a seperated thread + _incomingMessageQueue.Add(e.Message); + } + + private void CommunicationChannel_StateChanged(ICommunicationChannel sender, CommunicationStateChangedEventArgs e) + { + //Process only Closed event + if (sender.State != CommunicationStates.Closed) + { + return; + } + + //Pulse waiting threads for incoming messages, because disconnected to the server, and can not receive message anymore. + lock (_waitingMessages) + { + foreach (var waitingMessage in _waitingMessages.Values) + { + waitingMessage.WaitEvent.Set(); + } + + _waitingMessages.Clear(); + } + } + + /// + /// This event handles processing messages when a message is added to queue (_incomingMessageQueue). + /// + /// Reference to message queue + /// Event arguments + private void IncomingMessageQueue_ProcessItem(object sender, ProcessQueueItemEventArgs e) + { + try + { + if (e.ProcessItem.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSControllerMessage && ControlMessageReceived != null) + { + var controllerMessage = e.ProcessItem as MDSControllerMessage; + if (controllerMessage == null) + { + return; + } + + ControlMessageReceived(this, new ControlMessageReceivedEventArgs(DeserializeControlMessage(controllerMessage))); + } + } + catch(Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// This method is called by _reconnectTimer_Tick to reconnect MDS server if disconnected. + /// + /// This argument is not used + private void ReconnectTimer_Tick(object state) + { + try + { + _reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite); + + //Send Ping message if connected and needed to send a Ping message + if (_running && IsConnectedToServer()) + { + SendPingMessageIfNeeded(); + } + + //Reconnect if disconnected + if (_running && !IsConnectedToServer()) + { + ConnectAndRegister(); + } + } + catch + { + //No action on error case + } + finally + { + lock (_reconnectTimer) + { + if (_running) + { + _reconnectTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + } + } + } + + /// + /// Sends a Ping message to MDS server if 60 seconds passed after last communication. + /// + private void SendPingMessageIfNeeded() + { + var now = DateTime.Now; + if (now.Subtract(LastIncomingMessageTime).TotalSeconds < 60 && + now.Subtract(LastOutgoingMessageTime).TotalSeconds < 60) + { + return; + } + + try + { + SendMessageInternal(new MDSPingMessage()); + } + catch + { + + } + } + + /// + /// Closes communication channel, thus disconnects from MDS server if it is connected. + /// + private void CloseCommunicationChannel() + { + try + { + _communicationChannel.Disconnect(); + } + catch + { + + } + } + + /// + /// Checks if client application is connected to MDS server. + /// + /// True, if connected. + private bool IsConnectedToServer() + { + return (_communicationChannel != null && _communicationChannel.State == CommunicationStates.Connected); + } + + /// + /// Deserializes a ControlMessage from a MDSControllerMessage. + /// + /// MDSControllerMessage that includes ControlMessage + /// Deserialized ControlMessage object. + private static ControlMessage DeserializeControlMessage(MDSControllerMessage controllerMessage) + { + return MDSSerializationHelper.DeserializeFromByteArray( + () => + ControlMessageFactory.CreateMessageByTypeId(controllerMessage.ControllerMessageTypeId), + controllerMessage.MessageData); + } + + #endregion + + #region Sub classes + + /// + /// This class is used as item in _waitingMessages collection. + /// Key: Message ID to wait response. + /// Value: ManualResetEvent to wait thread until response received. + /// + /// + private class WaitingMessage + { + /// + /// ManualResetEvent to wait thread until response received. + /// + public ManualResetEvent WaitEvent { get; private set; } + + /// + /// Response message received for sent message + /// + public MDSControllerMessage ResponseMessage { get; set; } + + /// + /// Creates a new WaitingMessage. + /// + public WaitingMessage() + { + WaitEvent = new ManualResetEvent(false); + } + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Properties/AssemblyInfo.cs b/src/MDSCommonLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b34e87a --- /dev/null +++ b/src/MDSCommonLib/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MDSCommonLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("SESTEK A.Ş.")] +[assembly: AssemblyProduct("MDSCommonLib")] +[assembly: AssemblyCopyright("Copyright © SESTEK A.Ş. 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: log4net.Config.XmlConfigurator(Watch = true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d1ee96d4-0c33-497e-b5d9-e44eac66af87")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.0.0")] +[assembly: AssemblyFileVersion("0.9.0.0")] diff --git a/src/MDSCommonLib/Serialization/CreateSerializableObjectHandler.cs b/src/MDSCommonLib/Serialization/CreateSerializableObjectHandler.cs new file mode 100644 index 0000000..c3e9b8d --- /dev/null +++ b/src/MDSCommonLib/Serialization/CreateSerializableObjectHandler.cs @@ -0,0 +1,31 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Serialization +{ + /// + /// This delegate is used with IMDSDeserializer to deserialize an object. + /// It is used by IMDSDeserializer to create an instance of deserializing object. + /// So, user of MDS serialization must supply a method that creates an empty T object. + /// This is needed for performance reasons. Because it is slower to create object by reflection. + /// + /// Type of the object to be deserialized + /// An object from type T + public delegate T CreateSerializableObjectHandler() where T : IMDSSerializable; +} diff --git a/src/MDSCommonLib/Serialization/IMDSDeserializer.cs b/src/MDSCommonLib/Serialization/IMDSDeserializer.cs new file mode 100644 index 0000000..d249d4e --- /dev/null +++ b/src/MDSCommonLib/Serialization/IMDSDeserializer.cs @@ -0,0 +1,106 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Serialization +{ + /// + /// This interface is used to deserialize primitives and objects. + /// Only needed Read methods designed for MDS. + /// + public interface IMDSDeserializer + { + /// + /// Deserializes and returns a serialized byte. + /// + /// Deserialized byte + byte ReadByte(); + + /// + /// Reads a byte array from deserializing stream. + /// Created byte array may be null or empty. + /// + /// Deserialized string + byte[] ReadByteArray(); + + /// + /// Deserializes and returns a serialized integer. + /// + /// Deserialized integer + int ReadInt32(); + + /// + /// Deserializes and returns a serialized unsigned integer. + /// + /// Deserialized unsigned integer + uint ReadUInt32(); + + /// + /// Deserializes and returns a serialized long. + /// + /// Deserialized long + long ReadInt64(); + + /// + /// Deserializes and returns a serialized boolean. + /// + /// Deserialized boolean + bool ReadBoolean(); + + /// + /// Deserializes and returns a serialized DateTime object. + /// + /// Deserialized DateTime object + DateTime ReadDateTime(); + + /// + /// Deserializes and returns a serialized char using UTF8. + /// + /// Deserialized char + char ReadCharUTF8(); + + /// + /// Deserializes and returns a serialized string using UTF8. + /// Created string may be null or empty. + /// + /// Deserialized string + string ReadStringUTF8(); + + /// + /// Deserializes and returns an object that implements IMDSSerializable. + /// Object creation method is passed as parameter and used to create empty object. + /// Created object may be null. + /// + /// A class that implements IMDSSerializable + /// A function that creates an empty T object + /// Deserialized object + T ReadObject(CreateSerializableObjectHandler createObjectHandler) where T : IMDSSerializable; + + /// + /// Deserializes and returns an array of objects that implements IMDSSerializable. + /// Object creation method is passed as parameter and used to create empty object. + /// Created array may be null or empty. + /// + /// A class that implements IMDSSerializable + /// A function that creates an empty T object + /// Deserialized object + T[] ReadObjectArray(CreateSerializableObjectHandler createObjectHandler) where T : IMDSSerializable; + } +} diff --git a/src/MDSCommonLib/Serialization/IMDSSerializable.cs b/src/MDSCommonLib/Serialization/IMDSSerializable.cs new file mode 100644 index 0000000..d5f17a1 --- /dev/null +++ b/src/MDSCommonLib/Serialization/IMDSSerializable.cs @@ -0,0 +1,39 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Serialization +{ + /// + /// This interface is implemented by all classes that can be serialized/deserialized by MDS Serialization. + /// + public interface IMDSSerializable + { + /// + /// This method serializes the object. + /// + /// Used to serialize object + void Serialize(IMDSSerializer serializer); + + /// + /// This method deserializes the object. + /// + /// Used to deserialize object + void Deserialize(IMDSDeserializer deserializer); + } +} diff --git a/src/MDSCommonLib/Serialization/IMDSSerializer.cs b/src/MDSCommonLib/Serialization/IMDSSerializer.cs new file mode 100644 index 0000000..26ced5a --- /dev/null +++ b/src/MDSCommonLib/Serialization/IMDSSerializer.cs @@ -0,0 +1,101 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Serialization +{ + /// + /// This interface is used to serialize primitives and objects. + /// Only needed Write methods designed for MDS. + /// + public interface IMDSSerializer + { + /// + /// Serializes a byte. + /// + /// byte to serialize + void WriteByte(byte b); + + /// + /// Writes a byte array to serialization stream. + /// Byte array may be null or empty. + /// + /// byte array to write + void WriteByteArray(byte[] bytes); + + /// + /// Serializes an integer. + /// + /// integer to serialize + void WriteInt32(int number); + + /// + /// Serializes an unsigned integer. + /// + /// unsigned integer to serialize + void WriteUInt32(uint number); + + /// + /// Serializes a long. + /// + /// long to serialize + void WriteInt64(long number); + + /// + /// Serializes a boolean. + /// + /// boolean to serialize + void WriteBoolean(bool b); + + /// + /// Serializes a DateTime object. + /// + /// DateTime to serialize + void WriteDateTime(DateTime dateTime); + + /// + /// Serializes a char according to UTF8. + /// Char may be null or empty. + /// + /// char to serialize + void WriteCharUTF8(char c); + + /// + /// Serializes a string according to UTF8. + /// String may be null or empty. + /// + /// string to serialize + void WriteStringUTF8(string text); + + /// + /// Serializes an object that implements IMDSSerializable interface. + /// Object may be null. + /// + /// object to serialize + void WriteObject(IMDSSerializable serializableObject); + + /// + /// Serializes an array that all items implements IMDSSerializable interface. + /// Object array may be null or empty. + /// + /// objects to serialize + void WriteObjectArray(IMDSSerializable[] serializableObjects); + } +} diff --git a/src/MDSCommonLib/Serialization/MDSDefaultDeserializer.cs b/src/MDSCommonLib/Serialization/MDSDefaultDeserializer.cs new file mode 100644 index 0000000..c8ec3ad --- /dev/null +++ b/src/MDSCommonLib/Serialization/MDSDefaultDeserializer.cs @@ -0,0 +1,265 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.IO; +using System.Text; +using MDS.Exceptions; + +namespace MDS.Serialization +{ + /// + /// This class is the default deserializer of MDS. + /// The deserializing object must be serialized by MDSDefaultSerializer. + /// Only needed deserializers designed for MDS. + /// + public class MDSDefaultDeserializer : IMDSDeserializer + { + #region Private fields + + /// + /// The stream that is used to read serialized items for deserializing. + /// + private readonly Stream _stream; + + #endregion + + #region Public methods + + /// + /// Creates a new MDSDefaultDeserializer object. + /// + /// The stream that is used to read serialized items for deserializing + public MDSDefaultDeserializer(Stream stream) + { + _stream = stream; + } + + /// + /// Deserializes and returns a serialized byte. + /// + /// Deserialized byte + public byte ReadByte() + { + var b = _stream.ReadByte(); + if (b == -1) + { + ThrowEndOfStreamException(); + } + + return (byte)b; + } + + /// + /// Reads a byte array from deserializing stream. + /// Created byte array may be null or empty. + /// + /// Deserialized string + public byte[] ReadByteArray() + { + var length = ReadInt32(); + if (length < 0) + { + return null; + } + + if (length == 0) + { + return new byte[0]; + } + + return ReadByteArray(length); + } + + /// + /// Deserializes and returns a serialized integer. + /// + /// Deserialized integer + public int ReadInt32() + { + return ((ReadByte() << 24) | + (ReadByte() << 16) | + (ReadByte() << 8) | + (ReadByte()) + ); + } + + /// + /// Deserializes and returns a serialized unsigned integer. + /// + /// Deserialized unsigned integer + public uint ReadUInt32() + { + return (uint) ReadInt32(); + } + + /// + /// Deserializes and returns a serialized long. + /// + /// Deserialized long + public long ReadInt64() + { + return (((long)ReadByte() << 56) | + ((long)ReadByte() << 48) | + ((long)ReadByte() << 40) | + ((long)ReadByte() << 32) | + ((long)ReadByte() << 24) | + ((long)ReadByte() << 16) | + ((long)ReadByte() << 8) | + ((long)ReadByte()) + ); + } + + /// + /// Deserializes and returns a serialized boolean. + /// + /// Deserialized boolean + public bool ReadBoolean() + { + return (ReadByte() == 1); + } + + /// + /// Deserializes and returns a serialized DateTime object. + /// + /// Deserialized DateTime object + public DateTime ReadDateTime() + { + return new DateTime(ReadInt64()); + } + + /// + /// Deserializes and returns a serialized char using UTF8. + /// Note: A better way may be found. + /// + /// Deserialized char + public char ReadCharUTF8() + { + return ReadStringUTF8()[0]; + } + + /// + /// Deserializes and returns a serialized string using UTF8. + /// Created string may be null or empty. + /// + /// Deserialized string + public string ReadStringUTF8() + { + var length = ReadInt32(); + if (length < 0) + { + return null; + } + + if (length == 0) + { + return ""; + } + + return Encoding.UTF8.GetString(ReadByteArray(length), 0, length); + } + + /// + /// Deserializes and returns an object that implements IMDSSerializable. + /// Object creation method is passed as parameter and used to create empty object. + /// Created object may be null. + /// + /// A class that implements IMDSSerializable + /// A function that creates an empty T object + /// Deserialized object + public T ReadObject(CreateSerializableObjectHandler createObjectHandler) where T : IMDSSerializable + { + if (ReadByte() == 0) + { + return default(T); + } + + var serializableObject = createObjectHandler(); + serializableObject.Deserialize(this); + return serializableObject; + } + + /// + /// Deserializes and returns an array of objects that implements IMDSSerializable. + /// Object creation method is passed as parameter and used to create empty object. + /// Created array may be null or empty. + /// + /// A class that implements IMDSSerializable + /// A function that creates an empty T object + /// Deserialized object + public T[] ReadObjectArray(CreateSerializableObjectHandler createObjectHandler) where T : IMDSSerializable + { + var length = ReadInt32(); + if (length < 0) + { + return null; + } + + if (length == 0) + { + return new T[0]; + } + + var objects = new T[length]; + for (var i = 0; i < length; i++) + { + objects[i] = ReadObject(createObjectHandler); + } + + return objects; + } + + #endregion + + #region Private methods + + /// + /// Reads a byte array with spesified length. + /// + /// Length of the byte array to read + /// Read byte array + private byte[] ReadByteArray(int length) + { + var buffer = new byte[length]; + var totalRead = 0; + while (totalRead < length) + { + var read = _stream.Read(buffer, totalRead, length - totalRead); + if (read <= 0) + { + ThrowEndOfStreamException(); + } + + totalRead += read; + } + + return buffer; + } + + /// + /// Throws an exception for + /// + private static void ThrowEndOfStreamException() + { + throw new MDSSerializationException("Can not read from stream! Input stream is closed."); + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Serialization/MDSDefaultSerializer.cs b/src/MDSCommonLib/Serialization/MDSDefaultSerializer.cs new file mode 100644 index 0000000..165ab44 --- /dev/null +++ b/src/MDSCommonLib/Serialization/MDSDefaultSerializer.cs @@ -0,0 +1,210 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.IO; +using System.Text; + +namespace MDS.Serialization +{ + /// + /// This class is the default serializer of MDS. + /// The serialized object must be deserialized by MDSDefaultDeserializer. + /// Only needed serializers designed for MDS. + /// + public class MDSDefaultSerializer : IMDSSerializer + { + #region Private fields + + /// + /// The stream that is used to write serialized items. + /// + private readonly Stream _stream; + + #endregion + + #region Public methods + + /// + /// Creates a new MDSDefaultSerializer object. + /// + /// The stream that is used to write serialized items + public MDSDefaultSerializer(Stream stream) + { + _stream = stream; + } + + /// + /// Serializes a byte. + /// + /// byte to serialize + public void WriteByte(byte b) + { + _stream.WriteByte(b); + } + + /// + /// Writes a byte array to serialization stream. + /// Byte array may be null or empty. + /// + /// byte array to write + public void WriteByteArray(byte[] bytes) + { + if (bytes == null) + { + WriteInt32(-1); + } + else + { + WriteInt32(bytes.Length); + if (bytes.Length > 0) + { + _stream.Write(bytes, 0, bytes.Length); + } + } + } + + /// + /// Serializes a integer. + /// + /// integer to serialize + public void WriteInt32(int number) + { + _stream.WriteByte((byte)((number >> 24) & 0xFF)); + _stream.WriteByte((byte)((number >> 16) & 0xFF)); + _stream.WriteByte((byte)((number >> 8) & 0xFF)); + _stream.WriteByte((byte)(number & 0xFF)); + } + + /// + /// Serializes an unsigned integer. + /// + /// unsigned integer to serialize + public void WriteUInt32(uint number) + { + WriteInt32((int) number); + } + + /// + /// Serializes a long. + /// + /// long to serialize + public void WriteInt64(long number) + { + _stream.WriteByte((byte)((number >> 56) & 0xFF)); + _stream.WriteByte((byte)((number >> 48) & 0xFF)); + _stream.WriteByte((byte)((number >> 40) & 0xFF)); + _stream.WriteByte((byte)((number >> 32) & 0xFF)); + _stream.WriteByte((byte)((number >> 24) & 0xFF)); + _stream.WriteByte((byte)((number >> 16) & 0xFF)); + _stream.WriteByte((byte)((number >> 8) & 0xFF)); + _stream.WriteByte((byte)(number & 0xFF)); + } + + /// + /// Serializes a boolean. + /// + /// boolean to serialize + public void WriteBoolean(bool b) + { + _stream.WriteByte((byte) (b ? 1 : 0)); + } + + /// + /// Serializes a DateTime object. + /// + /// DateTime to serialize + public void WriteDateTime(DateTime dateTime) + { + WriteInt64(dateTime.Ticks); + } + + /// + /// Serializes a char according to UTF8. + /// Char may be null or empty. + /// Note: A better way may be found. + /// + /// char to serialize + public void WriteCharUTF8(char c) + { + WriteByteArray(Encoding.UTF8.GetBytes(c.ToString())); + } + + /// + /// Serializes a string according to UTF8. + /// String may be null or empty. + /// + /// string to serialize + public void WriteStringUTF8(string text) + { + switch (text) + { + case null: + WriteInt32(-1); + break; + case "": + WriteInt32(0); + break; + default: + WriteByteArray(Encoding.UTF8.GetBytes(text)); + break; + } + } + + /// + /// Serializes an object that implements IMDSSerializable interface. + /// Object may be null. + /// + /// object to serialize + public void WriteObject(IMDSSerializable serializableObject) + { + if (serializableObject == null) + { + _stream.WriteByte(0); + return; + } + + _stream.WriteByte(1); + serializableObject.Serialize(this); + } + + /// + /// Serializes an array that all items implements IMDSSerializable interface. + /// Object array may be null or empty. + /// + /// objects to serialize + public void WriteObjectArray(IMDSSerializable[] serializableObjects) + { + if (serializableObjects == null) + { + WriteInt32(-1); + } + else + { + WriteInt32(serializableObjects.Length); + for (var i = 0; i < serializableObjects.Length; i++) + { + WriteObject(serializableObjects[i]); + } + } + } + + #endregion + } +} diff --git a/src/MDSCommonLib/Serialization/MDSSerializationHelper.cs b/src/MDSCommonLib/Serialization/MDSSerializationHelper.cs new file mode 100644 index 0000000..b21fa9b --- /dev/null +++ b/src/MDSCommonLib/Serialization/MDSSerializationHelper.cs @@ -0,0 +1,75 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.IO; + +namespace MDS.Serialization +{ + /// + /// This class is used to simplify serialization/deserialization with MDS serialization classes. + /// + public static class MDSSerializationHelper + { + /// + /// Serializes an object that implements IMDSSerializable and returns serialized byte array. + /// + /// Object to serialize + /// Serialized object as byte array + public static byte[] SerializeToByteArray(IMDSSerializable serializableObject) + { + var stream = new MemoryStream(); + new MDSDefaultSerializer(stream).WriteObject(serializableObject); + return stream.ToArray(); + } + + /// + /// Serializes an object that implements IMDSSerializable to a Stream. + /// + /// Stream to write serialized object + /// Object to serialize + public static void SerializeToStream(Stream stream, IMDSSerializable serializableObject) + { + new MDSDefaultSerializer(stream).WriteObject(serializableObject); + } + + /// + /// Deserializes an object from a byte array. + /// + /// Type of object. This type must implement IMDSSerializable interface + /// A function that creates an instance of that object (T) + /// Byte array + /// Deserialized object + public static T DeserializeFromByteArray(CreateSerializableObjectHandler createObjectHandler, byte[] bytesOfObject) where T : IMDSSerializable + { + return new MDSDefaultDeserializer(new MemoryStream(bytesOfObject) {Position = 0}).ReadObject(createObjectHandler); + } + + /// + /// Deserializes an object via reading from a stream. + /// + /// Type of object. This type must implement IMDSSerializable interface + /// A function that creates an instance of that object (T) + /// Deserialized object + /// Deserialized object + public static T DeserializeFromStream(CreateSerializableObjectHandler createObjectHandler, Stream stream) where T : IMDSSerializable + { + return new MDSDefaultDeserializer(stream).ReadObject(createObjectHandler); + } + } +} diff --git a/src/MDSCommonLib/Threading/IRunnable.cs b/src/MDSCommonLib/Threading/IRunnable.cs new file mode 100644 index 0000000..b14676e --- /dev/null +++ b/src/MDSCommonLib/Threading/IRunnable.cs @@ -0,0 +1,43 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Threading +{ + /// + /// This interface is used for a class that can startable and stoppable. + /// + public interface IRunnable + { + /// + /// This method is used to start running. + /// + void Start(); + + /// + /// This method is used to stop running. + /// + /// Indicates that caller thread waits stopping of module + void Stop(bool waitToStop); + + /// + /// Joins module's thread until it stops. + /// + void WaitToStop(); + } +} diff --git a/src/MDSCommonLib/Threading/ProcessQueueItemHandler.cs b/src/MDSCommonLib/Threading/ProcessQueueItemHandler.cs new file mode 100644 index 0000000..515b307 --- /dev/null +++ b/src/MDSCommonLib/Threading/ProcessQueueItemHandler.cs @@ -0,0 +1,59 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Threading +{ + /// + /// A delegate to used by QueueProcessorThread to raise processing event + /// + /// Type of the item to process + /// The object which raises event + /// Event arguments + public delegate void ProcessQueueItemHandler(object sender, ProcessQueueItemEventArgs e); + + /// + /// Stores processing item and some informations about queue. + /// + /// + public class ProcessQueueItemEventArgs : EventArgs + { + /// + /// The item to process. + /// + public T ProcessItem { get; set; } + + /// + /// The item count waiting for processing on queue (after this one). + /// + public int QueuedItemCount { get; set; } + + /// + /// Constructor. + /// + /// The item to process + /// The item count waiting for processing on queue (after this one) + public ProcessQueueItemEventArgs(T processItem, int queuedItemCount) + { + ProcessItem = processItem; + QueuedItemCount = queuedItemCount; + } + } +} \ No newline at end of file diff --git a/src/MDSCommonLib/Threading/QueueProcessorThread.cs b/src/MDSCommonLib/Threading/QueueProcessorThread.cs new file mode 100644 index 0000000..2efaba2 --- /dev/null +++ b/src/MDSCommonLib/Threading/QueueProcessorThread.cs @@ -0,0 +1,185 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections; +using System.Threading; + +namespace MDS.Threading +{ + /// + /// A threaded queue that process only one item in a time and keeps others in a queue. + /// + /// Type of the processing item + public class QueueProcessorThread : IRunnable + { + /// + /// This event is used to process get and process an item from queue. When an item inserted this + /// queue, ProcessItem event is raised. + /// + public event ProcessQueueItemHandler ProcessItem; + + /// + /// Queue object to store items. + /// + private readonly Queue _queue; + + /// + /// Running thread. + /// + private Thread _thread; + + /// + /// Thread control flag. + /// + private volatile bool _running; + + /// + /// Construnctor. + /// + public QueueProcessorThread() + { + _queue = Queue.Synchronized(new Queue()); + } + + /// + /// Starts the processing of items. Thread runs, listens and process items on queue. + /// + public void Start() + { + lock (_queue.SyncRoot) + { + if (_running) + { + return; + } + + _running = true; + _thread = new Thread(DoProcess); + _thread.Start(); + } + } + + /// + /// Stops the processing of items and stops the thread. + /// + /// True, if caller method must wait until running stops. + public void Stop(bool waitToStop) + { + lock (_queue.SyncRoot) + { + if (_running) + { + _running = false; + Monitor.PulseAll(_queue.SyncRoot); + } + } + + if(waitToStop) + { + WaitToStop(); + } + } + + /// + /// Waits stopping of thread, thus waits end of execution of currently processing item. + /// + public void WaitToStop() + { + if (_thread == null) + { + return; + } + + try + { + _thread.Join(); + } + catch + { + + } + } + + /// + /// Adds given item to queue to process. + /// + /// + public void Add(T queueItem) + { + lock (_queue.SyncRoot) + { + _queue.Enqueue(queueItem); + Monitor.PulseAll(_queue.SyncRoot); + } + } + + /// + /// Thread's running method. Listens queue and processes items. + /// + private void DoProcess() + { + while (_running) + { + var queueItem = default(T); + var remainingItemCount = 0; + lock (_queue.SyncRoot) + { + if (_queue.Count > 0) + { + queueItem = (T)_queue.Dequeue(); + remainingItemCount = _queue.Count; + } + else + { + Monitor.Wait(_queue.SyncRoot); + } + } + + if (!Equals(queueItem, default(T))) + { + OnProcessItem(queueItem, remainingItemCount); + } + } + + _thread = null; + } + + /// + /// This method is used to raise ProcessItem event. + /// + /// The item that must be processed + /// Waiting item count on queue except this one + protected virtual void OnProcessItem(T queueItem, int remainingItemCount) + { + if (ProcessItem == null) + { + return; + } + + try + { + ProcessItem(this, new ProcessQueueItemEventArgs(queueItem, remainingItemCount)); + } + catch + { + + } + } + } +} diff --git a/src/MDSCommonLib/Utils/MDSObjectCollection.cs b/src/MDSCommonLib/Utils/MDSObjectCollection.cs new file mode 100644 index 0000000..b684fbb --- /dev/null +++ b/src/MDSCommonLib/Utils/MDSObjectCollection.cs @@ -0,0 +1,95 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; + +namespace MDS.Utils +{ + /// + /// Represents a thread-safe string-key based object collection. + /// + public class MDSObjectCollection + { + /// + /// All objects are stored in this collection. + /// + private readonly SortedDictionary _objects; + + /// + /// Gets the synchronization object that is used by + /// + public object SyncObj { get; private set; } + + /// + /// Contructor. + /// + public MDSObjectCollection() + { + SyncObj = new object(); + _objects = new SortedDictionary(); + } + + /// + /// Gets/sets an object with given key. + /// + /// Key + /// Object with given key, or null if key does not exists + public object this[string key] + { + get + { + lock (SyncObj) + { + return _objects.ContainsKey(key) ? _objects[key] : null; + } + } + + set + { + lock (SyncObj) + { + _objects[key] = value; + } + } + } + + /// + /// Removes an object from collection. + /// + /// Key of object to remove + public void Remove(string key) + { + lock (SyncObj) + { + _objects.Remove(key); + } + } + + /// + /// Rewmoves all keys/values from collection. + /// + public void Clear() + { + lock (SyncObj) + { + _objects.Clear(); + } + } + } +} diff --git a/src/MDSCommonLib/Utils/RegistrySettings.cs b/src/MDSCommonLib/Utils/RegistrySettings.cs new file mode 100644 index 0000000..190da28 --- /dev/null +++ b/src/MDSCommonLib/Utils/RegistrySettings.cs @@ -0,0 +1,257 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using Microsoft.Win32; + +namespace MDS.Utils +{ + /// + /// This class can be used to set/get settings to/from a registry key with caching capability. + /// + public class RegistrySettings + { + #region Public fields + + /// + /// Registry key to store settings of application. + /// + public string RegistryKey + { + get { return _registryKey; } + set { _registryKey = value; } + } + private string _registryKey; + + /// + /// Indicates that if RegistrySettings uses caching. + /// + public bool Caching + { + get { return _caching; } + set { _caching = value; } + } + private bool _caching; + + #endregion + + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Cached settings. + /// + private readonly SortedList _cache; + + #endregion + + #region Constructor + + /// + /// Creates a new RegistrySettings instance. + /// + public RegistrySettings(string registryKey) + { + _registryKey = registryKey; + _cache = new SortedList(); + } + + #endregion + + #region Public methods + + /// + /// Gets an integer value from registry. It gets from cache if it is cached before and Caching enables. + /// + /// Value Name on the registry key + /// Default value, if no value exists on registry + /// Value for registryName + public int GetIntegerValue(string registryName, int defaultValue) + { + if ((!_caching) || (!_cache.ContainsKey(registryName))) + { + try + { + _cache[registryName] = Convert.ToInt32(GetObjectFromRegistry(registryName, defaultValue, true)); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + _cache[registryName] = defaultValue; + } + } + + return (int)_cache[registryName]; + } + + /// + /// Sets an integer value to registry. + /// + /// Value Name on the registry key + /// New value to set + public void SetIntegerValue(string registryName, int value) + { + SetValue(registryName, value); + } + + /// + /// Gets an string value from registry. It gets from cache if it is cached before and Caching enables. + /// + /// Value Name on the registry key + /// Default value, if no value exists on registry + /// Value for registryName + public string GetStringValue(string registryName, string defaultValue) + { + if ((!_caching) || (!_cache.ContainsKey(registryName))) + { + try + { + _cache[registryName] = GetObjectFromRegistry(registryName, defaultValue, true); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + _cache[registryName] = defaultValue; + } + } + + return _cache[registryName] as string; + } + + /// + /// Sets a string value to registry. + /// + /// Value Name on the registry key + /// New value to set + public void SetStringValue(string registryName, string value) + { + SetValue(registryName, value); + } + + /// + /// Sets a value to registry. + /// + /// Value Name on the registry key + /// New value to set + public void SetValue(string registryName, object value) + { + _cache[registryName] = value; + try + { + SetObjectOnRegistry(registryName, value); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + #endregion + + #region Private methods + + /// + /// Creates a registry key. + /// + /// Registry key to create + /// Created registry key + private static RegistryKey CreateKey(string registryKey) + { + var regKey = Registry.LocalMachine.CreateSubKey(registryKey); + if (regKey == null) + { + throw new Exception("Registry key '" + registryKey + "' can no be created."); + } + return regKey; + } + + /// + /// Tries open a registry key and return it as writable/readable. + /// If key doesn't exists then creates and returns it. + /// + /// Registry key + /// Registry key + private static RegistryKey OpenOrCreateKey(string registryKey) + { + return Registry.LocalMachine.OpenSubKey(registryKey, true) ?? CreateKey(registryKey); + } + + /// + /// Gets a value from registry. + /// + /// Name in registry key + /// Default value that is returned if name can not be found in registry key + /// Value of name entry in registry key + private object GetObjectFromRegistry(string name, object defaultValue) + { + return GetObjectFromRegistry(name, defaultValue, false); + } + + /// + /// Gets a value from registry. + /// + /// Name in registry key + /// Default value that is returned if name can not be found in registry key + /// If this is true and registryKey is not exists, then it is created + /// Value of name entry in registry key + private object GetObjectFromRegistry(string name, object defaultValue, bool createIfNeeded) + { + var regKey = createIfNeeded ? OpenOrCreateKey(_registryKey) : Registry.LocalMachine.OpenSubKey(_registryKey); + if (regKey == null) + { + throw new Exception("Registry key '" + _registryKey + "' can not be open."); + } + try + { + return regKey.GetValue(name, defaultValue); + } + finally + { + regKey.Close(); + } + } + + /// + /// Sets a value on registry. + /// + /// Name in registry key + /// value to set + private void SetObjectOnRegistry(string name, object value) + { + var regKey = OpenOrCreateKey(_registryKey); + try + { + regKey.SetValue(name, value); + } + finally + { + regKey.Close(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MDSCore/Changes.txt b/src/MDSCore/Changes.txt new file mode 100644 index 0000000..d377b1f --- /dev/null +++ b/src/MDSCore/Changes.txt @@ -0,0 +1,5 @@ +# Version 0.9.1.0 - 23.05.2011 - Halil İbrahim KALKAN + Added MS SQL Server Support as Storage (Database) Manager. + +# Version 0.9.0.0 - 09.05.2011 - Halil İbrahim KALKAN + First release. \ No newline at end of file diff --git a/src/MDSCore/Communication/CommunicationLayer.cs b/src/MDSCore/Communication/CommunicationLayer.cs new file mode 100644 index 0000000..1d4b7ad --- /dev/null +++ b/src/MDSCore/Communication/CommunicationLayer.cs @@ -0,0 +1,437 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication.Events; +using MDS.Communication.TCPCommunication; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Settings; +using MDS.Threading; + +namespace MDS.Communication +{ + /// + /// Represents communication layer of MDS server. This class represents communicators as servers and applications + /// to upper layers. + /// + public class CommunicationLayer : IRunnable + { + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to the settings. + /// + private readonly MDSSettings _settings; + + /// + /// A collection that stores all remote applications. + /// key: ApplicationId + /// Total MDSRemoteApplication objects count is equal to + /// (MDSClientApplication count + MDSAdjacentServer count + 1 MDSController). + /// + private readonly SortedList _remoteApplications; + + /// + /// A collection that stores communication managers. + /// + private readonly List _communicationManagers; + + /// + /// Temporary Communicator List. This list store a communicator until that communicator + /// registers to MDS server. After registration, it is removed from list. + /// + private readonly SortedList _communicators; + + /// + /// Last generated application ID. This is used to get Unique ID for a RemoteApplication. + /// It is used by CreateApplicationId method. + /// + private static int _lastApplicationId; + + /// + /// Last generated communicator ID. When a new communicator builded, it gets + /// _lastCommunicatorId+1 by calling CreateCommunicatorId() method. + /// + private static long _lastCommunicatorId; + + #endregion + + #region Constructors + + /// + /// Constructor. + /// + public CommunicationLayer() + { + _settings = MDSSettings.Instance; + _remoteApplications = new SortedList(); + _communicators = new SortedList(); + _communicationManagers = + new List + { + new TCPCommunicationManager(Convert.ToInt32(_settings["__ThisServerTCPPort"].Trim())) + }; + + foreach (var manager in _communicationManagers) + { + manager.CommunicatorConnected += Manager_CommunicatorConnected; + } + } + + #endregion + + #region Public methods + + /// + /// Generates a Unique ID for Remote Applications. + /// + /// Unique ID + public static int CreateApplicationId() + { + return Interlocked.Increment(ref _lastApplicationId); + } + + /// + /// Generates a Unique ID for communicators. + /// + /// Unique ID + public static long CreateCommunicatorId() + { + return Interlocked.Increment(ref _lastCommunicatorId); + } + + /// + /// Starts the communication layer and all subsytems. + /// + public void Start() + { + foreach (var manager in _communicationManagers) + { + manager.Start(); + } + } + + /// + /// Stops the communication layer and all subsytems. + /// + /// Indicates that caller thread must wait + /// until communication layer stops + public void Stop(bool waitToStop) + { + foreach (var manager in _communicationManagers) + { + manager.Stop(waitToStop); + } + + StopCommunicators(waitToStop); + ClearCommunicators(waitToStop); + } + + /// + /// Waits until communication layer stops. + /// + public void WaitToStop() + { + foreach (var manager in _communicationManagers) + { + manager.WaitToStop(); + } + + WaitToStopOfCommunicators(); + ClearCommunicators(true); + } + + /// + /// Adds a remote application to communication layer. + /// + /// Remote application to add + public void AddRemoteApplication(MDSRemoteApplication application) + { + lock (_remoteApplications) + { + if (!_remoteApplications.ContainsKey(application.ApplicationId)) + { + _remoteApplications.Add(application.ApplicationId, application); + } + } + } + + /// + /// Removes a remote application from communication layer. + /// + /// Remote application to remove + public void RemoveRemoteApplication(MDSRemoteApplication application) + { + lock (_remoteApplications) + { + if (_remoteApplications.ContainsKey(application.ApplicationId)) + { + if(application.ConnectedCommunicatorCount > 0) + { + throw new MDSException("Remote application can not be removed. It has " + + application.ConnectedCommunicatorCount + " communicators connected."); + } + + _remoteApplications.Remove(application.ApplicationId); + } + } + } + + #endregion + + #region Private methods + + /// + /// When a communicator connects to server, this method is called. + /// + /// Sender + /// Event args + private void Manager_CommunicatorConnected(object sender, CommunicatorConnectedEventArgs e) + { + e.Communicator.StateChanged += Communicator_StateChanged; + AddToCommunicators(e.Communicator); + e.Communicator.MessageReceived += Communicator_MessageReceived; + e.Communicator.Start(); + } + + #region Register message handling and processing methods + + /// + /// When a message received from a communicator, this method is called. + /// This method just process Register messages. After a register message received + /// from cummunicator, stops listen to events from this communicator anymore. + /// + /// Sender (ICommunicator) + /// Event args + private void Communicator_MessageReceived(object sender, MessageReceivedFromCommunicatorEventArgs e) + { + if (e.Message.MessageTypeId != MDSMessageFactory.MessageTypeIdMDSRegisterMessage) + { + return; + } + + try + { + ProcessRegisterMessage(e.Communicator, e.Message as MDSRegisterMessage); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + finally + { + e.Communicator.MessageReceived -= Communicator_MessageReceived; + } + } + + /// + /// Processes MDSRegisterMessage objects. + /// + /// Sender communicator of message + /// Message + private void ProcessRegisterMessage(ICommunicator communicator, MDSRegisterMessage message) + { + //Set the communicator properties + communicator.CommunicationWay = message.CommunicationWay; + + MDSRemoteApplication remoteApplication = null; + //Find remote application + lock (_remoteApplications) + { + foreach (var app in _remoteApplications.Values) + { + if (app.Name == message.Name && message.CommunicatorType == app.CommunicatorType) + { + remoteApplication = app; + break; + } + } + } + + //If application is found... + if (remoteApplication != null) + { + try + { + //Add communicator to communicator list of remote application + remoteApplication.AddCommunicator(communicator); + //Remove communicator from tempoary communicators list. + RemoveFromCommunicators(communicator.ComminicatorId); + //Send success message to remote application + SendOperationResultMessage(communicator, true, communicator.ComminicatorId.ToString(), message.MessageId); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + //An error occured, send failed message to remote application + SendOperationResultMessage(communicator, false, ex.Message, message.MessageId); + communicator.Stop(false); + } + } + else //application == null + { + //Stop communicator, because a remote application can not connect this server that is not defined in settings file + SendOperationResultMessage(communicator, false, "No remote application found with name: " + message.Name, message.MessageId); + communicator.Stop(false); + } + } + + /// + /// Sends a MDSOperationResultMessage message to a communicator. + /// + /// Communicator object + /// Operation result + /// Detailed result/error text + /// The message id of request message + private static void SendOperationResultMessage(ICommunicator communicator, bool success, string resultText, string repliedMessageId) + { + communicator.SendMessage( + new MDSOperationResultMessage + { + Success = success, + ResultText = resultText, + RepliedMessageId = repliedMessageId + }); + } + + #endregion + + #region Communicator add/remove and stop methods + + /// + /// When state of a communicator changes, this method handles event. + /// It is used to remove a communicator from list when it is closed. + /// + /// Sender (ICommunicationManager) + /// Event arguments + private void Communicator_StateChanged(object sender, CommunicatorStateChangedEventArgs e) + { + switch (e.Communicator.State) + { + case CommunicationStates.Closed: + RemoveFromCommunicators(e.Communicator.ComminicatorId); + break; + } + } + + /// + /// Adds a TCPCommunicator object to _communicators list. + /// + /// TCPCommunicator to be added + private void AddToCommunicators(ICommunicator communicator) + { + lock (_communicators) + { + _communicators[communicator.ComminicatorId] = communicator; + } + } + + /// + /// Removes a TCPCommunicator object from _communicators list. + /// + /// Id of TCPCommunicator to be removed + private void RemoveFromCommunicators(long comminicatorId) + { + lock (_communicators) + { + if (_communicators.ContainsKey(comminicatorId)) + { + _communicators.Remove(comminicatorId); + } + } + } + + /// + /// Stops all communicator connections. + /// + /// Indicates that caller thread waits stopping of communicators + private void StopCommunicators(bool waitToStop) + { + lock (_communicators) + { + var communicatorIds = _communicators.Keys.ToArray(); + foreach (var communicatorId in communicatorIds) + { + try + { + _communicators[communicatorId].Stop(waitToStop); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + } + } + + /// + /// Removes all TCPCommunicator objects from _communicators list. + /// + /// Indicates that caller thread waits stop + private void ClearCommunicators(bool waitToStop) + { + if (!waitToStop) + { + return; + } + + lock (_communicators) + { + _communicators.Clear(); + } + } + + /// + /// Waits all communicators to stop. + /// + private void WaitToStopOfCommunicators() + { + lock (_communicators) + { + var communicatorIds = _communicators.Keys.ToArray(); + foreach (var communicatorId in communicatorIds) + { + try + { + _communicators[communicatorId].WaitToStop(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + } + } + + #endregion + + #endregion + } +} diff --git a/src/MDSCore/Communication/CommunicationManagerBase.cs b/src/MDSCore/Communication/CommunicationManagerBase.cs new file mode 100644 index 0000000..e92c621 --- /dev/null +++ b/src/MDSCore/Communication/CommunicationManagerBase.cs @@ -0,0 +1,63 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +using MDS.Communication.Events; + +namespace MDS.Communication +{ + /// + /// Base class for communicator managers. + /// + public abstract class CommunicationManagerBase : ICommunicationManager + { + /// + /// This event is raised when a communicator connection established. + /// + public event CommunicatorConnectedHandler CommunicatorConnected; + + /// + /// Starts communication manager. + /// + public abstract void Start(); + + /// + /// Stops communication manager. + /// + /// Indicates that caller thread waits stopping of manager + public abstract void Stop(bool waitToStop); + + /// + /// Waits stopping of communication manager. + /// + public abstract void WaitToStop(); + + /// + /// Raises CommunicatorConnected event. + /// + /// Communicator + protected void OnCommunicatorConnected(ICommunicator communicator) + { + if (CommunicatorConnected != null) + { + CommunicatorConnected(this, new CommunicatorConnectedEventArgs {Communicator = communicator}); + } + } + } +} diff --git a/src/MDSCore/Communication/CommunicatorBase.cs b/src/MDSCore/Communication/CommunicatorBase.cs new file mode 100644 index 0000000..173a9b6 --- /dev/null +++ b/src/MDSCore/Communication/CommunicatorBase.cs @@ -0,0 +1,204 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Exceptions; + +namespace MDS.Communication +{ + /// + /// This is the case class for all communicators. + /// A communicator can also be directly implement ICommunicator, + /// but this class helps to build a communicator class with several helper ana generic methods/properties. + /// + public abstract class CommunicatorBase : ICommunicator + { + #region Events + + /// + /// This event is raised when the state of the communicator changes. + /// + public event CommunicatorStateChangedHandler StateChanged; + + /// + /// This event is raised when a MdsMessage received. + /// + public event MessageReceivedFromCommunicatorHandler MessageReceived; + + #endregion + + #region Public properties + + /// + /// Unique identifier for this communicator. + /// + public long ComminicatorId { get; private set; } + + /// + /// Communication way for this communicator. + /// + public CommunicationWays CommunicationWay { get; set; } + + /// + /// Connection state of communicator. + /// + public CommunicationStates State + { + get { return _state; } + } + private volatile CommunicationStates _state; + + #endregion + + #region Private fields + + /// + /// Used to send only one message in a time by locking. + /// + private readonly object _sendLock; + + #endregion + + #region Constructors + + /// + /// Constructor. + /// + /// Unique identifier for this communicator. + protected CommunicatorBase(long comminicatorId) + { + ComminicatorId = comminicatorId; + CommunicationWay = CommunicationWays.Send; + _state = CommunicationStates.Closed; + _sendLock = new object(); + } + + #endregion + + #region Public methods + + /// + /// Starts communication. + /// + public void Start() + { + if (State != CommunicationStates.Closed) + { + throw new MDSException("Communicator is already connected"); + } + + lock (_sendLock) + { + OnChangeState(CommunicationStates.Connecting); + StartCommunicaiton(); + OnChangeState(CommunicationStates.Connected); + } + } + + /// + /// Stops communication. + /// + public void Stop(bool waitToStop) + { + if (State != CommunicationStates.Connected) + { + return; + } + + OnChangeState(CommunicationStates.Closing); + try + { + StopCommunicaiton(waitToStop); + } + finally + { + OnChangeState(CommunicationStates.Closed); + } + } + + /// + /// Sends a message to the communicator. + /// + /// + public void SendMessage(MDSMessage message) + { + lock (_sendLock) + { + SendMessageInternal(message); + } + } + + #endregion + + #region Protected methods + + /// + /// Changes the state of the communicator and raises event + /// + /// New state + protected void OnChangeState(CommunicationStates newState) + { + var oldState = _state; + _state = newState; + if (StateChanged != null) + { + StateChanged(this, new CommunicatorStateChangedEventArgs {Communicator = this, OldState = oldState}); + } + } + + /// + /// When a MDSMessage received, this method is called from derived class. + /// + /// incoming message from communicator + protected void OnMessageReceived(MDSMessage message) + { + if (MessageReceived != null) + { + MessageReceived(this, new MessageReceivedFromCommunicatorEventArgs {Communicator = this, Message = message}); + } + } + + #endregion + + #region Abstract methods + + /// + /// Derived class must override this method to start the communication. + /// + protected abstract void StartCommunicaiton(); + + /// + /// Derived class must override this method to stop the communication. + /// + protected abstract void StopCommunicaiton(bool waitToStop); + + /// + /// Waits communicator to finish it's job. + /// + public abstract void WaitToStop(); + + /// + /// Derived class must override this method to send a message. + /// + protected abstract void SendMessageInternal(MDSMessage message); + + #endregion + } +} diff --git a/src/MDSCore/Communication/Events/CommunicatorConnectedEventArgs.cs b/src/MDSCore/Communication/Events/CommunicatorConnectedEventArgs.cs new file mode 100644 index 0000000..0910ad3 --- /dev/null +++ b/src/MDSCore/Communication/Events/CommunicatorConnectedEventArgs.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Communication.Events +{ + /// + /// A delegate to create events when a communicator connection established. + /// + /// The object which raises event + /// Event arguments + public delegate void CommunicatorConnectedHandler(object sender, CommunicatorConnectedEventArgs e); + + /// + /// Stores communicator reference. + /// + public class CommunicatorConnectedEventArgs : EventArgs + { + /// + /// Communicator. + /// + public ICommunicator Communicator { get; set; } + } +} diff --git a/src/MDSCore/Communication/Events/CommunicatorDisconnectedEventArgs.cs b/src/MDSCore/Communication/Events/CommunicatorDisconnectedEventArgs.cs new file mode 100644 index 0000000..8bcf44b --- /dev/null +++ b/src/MDSCore/Communication/Events/CommunicatorDisconnectedEventArgs.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Communication.Events +{ + /// + /// A delegate to create events when a communicator connection closed. + /// + /// The object which raises event + /// Event arguments + public delegate void CommunicatorDisconnectedHandler(object sender, CommunicatorDisconnectedEventArgs e); + + /// + /// Stores communicator reference. + /// + public class CommunicatorDisconnectedEventArgs : EventArgs + { + /// + /// Communicator. + /// + public ICommunicator Communicator { get; set; } + } +} diff --git a/src/MDSCore/Communication/Events/CommunicatorStateChangedEventArgs.cs b/src/MDSCore/Communication/Events/CommunicatorStateChangedEventArgs.cs new file mode 100644 index 0000000..1f4daab --- /dev/null +++ b/src/MDSCore/Communication/Events/CommunicatorStateChangedEventArgs.cs @@ -0,0 +1,46 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; + +namespace MDS.Communication.Events +{ + /// + /// A delegate to create events for changing state of a communicator. + /// + /// The object which raises event + /// Event arguments + public delegate void CommunicatorStateChangedHandler(object sender, CommunicatorStateChangedEventArgs e); + + /// + /// Stores informations about communicator and it's state. + /// + public class CommunicatorStateChangedEventArgs : EventArgs + { + /// + /// Communicator. + /// + public CommunicatorBase Communicator { get; set; } + + /// + /// The state of the communicator before change. + /// + public CommunicationStates OldState { get; set; } + } +} diff --git a/src/MDSCore/Communication/Events/MessageReceivedFromCommunicatorEventArgs.cs b/src/MDSCore/Communication/Events/MessageReceivedFromCommunicatorEventArgs.cs new file mode 100644 index 0000000..a98c16f --- /dev/null +++ b/src/MDSCore/Communication/Events/MessageReceivedFromCommunicatorEventArgs.cs @@ -0,0 +1,47 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; + +namespace MDS.Communication.Events +{ + /// + /// A delegate to create events when a message received from a communicator. + /// + /// The object which raises event + /// Event arguments + public delegate void MessageReceivedFromCommunicatorHandler(object sender, MessageReceivedFromCommunicatorEventArgs e); + + /// + /// Stores communicator and message informations. + /// + public class MessageReceivedFromCommunicatorEventArgs : EventArgs + { + /// + /// Communicator. + /// + public ICommunicator Communicator { get; set; } + + /// + /// Received message from communicator. + /// + public MDSMessage Message { get; set; } + } +} diff --git a/src/MDSCore/Communication/Events/MessageReceivedFromRemoteApplicationEventArgs.cs b/src/MDSCore/Communication/Events/MessageReceivedFromRemoteApplicationEventArgs.cs new file mode 100644 index 0000000..551c829 --- /dev/null +++ b/src/MDSCore/Communication/Events/MessageReceivedFromRemoteApplicationEventArgs.cs @@ -0,0 +1,51 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Communication.Events +{ + /// + /// A delegate to create events when a message received from a remote application. + /// + /// The object which raises event + /// Event arguments + public delegate void MessageReceivedFromRemoteApplicationHandler(object sender, MessageReceivedFromRemoteApplicationEventArgs e); + + /// + /// Stores informations about received message + /// + public class MessageReceivedFromRemoteApplicationEventArgs + { + /// + /// Remote Application. + /// + public MDSRemoteApplication Application { get; set; } + + /// + /// Communicator. + /// + public ICommunicator Communicator { get; set; } + + /// + /// Received message from communicator. + /// + public MDSMessage Message { get; set; } + } +} diff --git a/src/MDSCore/Communication/ICommunicationManager.cs b/src/MDSCore/Communication/ICommunicationManager.cs new file mode 100644 index 0000000..f3a52d5 --- /dev/null +++ b/src/MDSCore/Communication/ICommunicationManager.cs @@ -0,0 +1,35 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Events; +using MDS.Threading; + +namespace MDS.Communication +{ + /// + /// This interface is implemented by communcation managers. + /// + public interface ICommunicationManager : IRunnable + { + /// + /// This event is raised when a communicator is connected succesfully. + /// + event CommunicatorConnectedHandler CommunicatorConnected; + } +} diff --git a/src/MDSCore/Communication/ICommunicator.cs b/src/MDSCore/Communication/ICommunicator.cs new file mode 100644 index 0000000..247474b --- /dev/null +++ b/src/MDSCore/Communication/ICommunicator.cs @@ -0,0 +1,63 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Threading; + +namespace MDS.Communication +{ + /// + /// Communicators is used by upper layers by this interface, + /// all communicator classes must implement it. + /// + public interface ICommunicator : IRunnable + { + /// + /// This event is raised when the state of the communicator changes. + /// + event CommunicatorStateChangedHandler StateChanged; + + /// + /// This event is raised when a MdsMessage received. + /// + event MessageReceivedFromCommunicatorHandler MessageReceived; + + /// + /// Unique identifier for this communicator in this server. + /// + long ComminicatorId { get; } + + /// + /// Connection state of communicator. + /// + CommunicationStates State { get; } + + /// + /// Communication way for this communicator. + /// + CommunicationWays CommunicationWay { get; set; } + + /// + /// Sends a message to the communicator. + /// + /// + void SendMessage(MDSMessage message); + } +} diff --git a/src/MDSCore/Communication/MDSRemoteApplication.SubClasses.cs b/src/MDSCore/Communication/MDSRemoteApplication.SubClasses.cs new file mode 100644 index 0000000..549313a --- /dev/null +++ b/src/MDSCore/Communication/MDSRemoteApplication.SubClasses.cs @@ -0,0 +1,485 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Threading; +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Threading; + +namespace MDS.Communication +{ + /// + /// This file contains subclasses of MDSRemoteApplication. + /// It is created to reduce complexity of MDSRemoteApplication class. + /// + public abstract partial class MDSRemoteApplication + { + #region MessageDeliverer class + + /// + /// This class is used to deliver messages to remote application. + /// It is designed to seperate message sending/delivering process from other processes of MDSRemoteApplication class. + /// + private class MessageDeliverer :IRunnable + { + #region Private fields + + /// + /// Reference to the MDSRemoteApplication object that is used with this MessageDeliverer. + /// + private readonly MDSRemoteApplication _remoteApplication; + + /// + /// This Timer is used to check if a ACK/Reject timeout occured for a message. + /// Normally, when a communicator disconnected, if it was processing a message but not acknowledged yet, + /// the message is turns back to the queue to be sent to another communicator (see RemoveReceiver method). + /// But sometimes a communicator that processing messages may crash and can not send TCP close signal, so communicator may be supposed running.. + /// In this situation this timer periodically checks if a certain time passed but not ACK/Reject received from a communicator. + /// + private readonly Timer _asynchronMessageControlTimer; + + #endregion + + #region Constructor + + /// + /// Constructor. + /// + /// Reference to the MDSRemoteApplication object that is used with this MessageDeliverer + public MessageDeliverer(MDSRemoteApplication remoteApplication) + { + _remoteApplication = remoteApplication; + _asynchronMessageControlTimer = new Timer(AsynchronMessageControlTimer_Elapsed, null, Timeout.Infinite, Timeout.Infinite); + } + + #endregion + + #region Public methods + + /// + /// Starts MessageDeliverer. + /// + public void Start() + { + _asynchronMessageControlTimer.Change(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + } + + /// + /// Stops MessageDeliverer. + /// + /// Wait stopping of MessageDeliverer + public void Stop(bool waitToStop) + { + lock (_asynchronMessageControlTimer) + { + _asynchronMessageControlTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + + /// + /// Waits stopping of MessageDeliverer. + /// + public void WaitToStop() + { + //No action + } + + /// + /// This method is used to send a MDSDataTransferMessage to an available communicator of application. + /// It just blocks caller thread until a communicator comes available and message is sent or until timeout, + /// but receives result (ACK/Reject) message asynchronous. It sends result (ACK/Reject) message to OnResponseReceived() method. + /// + /// Message to send + /// Timeout value to wait if all receivers are busy. Set 0 to do not use timeout. + /// True, if message sent to communicator. + public void SendDataMessage(MDSDataTransferMessage message, int timeOut) + { + //Get a free/available communicator from _receiverCommunicators list + ConnectedCommunicator receiver = null; + lock (_remoteApplication._communicators) + { + var startTime = DateTime.Now; + var passedTime = 0; + while (receiver == null) + { + //If no receiver is connected to server, throw exception + if (_remoteApplication._communicators.Count <= 0) + { + throw new MDSNoCommunicatorException("There is no communicator for remote application '" + _remoteApplication.Name + "' to send message."); + } + + //If timeout is set and timeout occurs, throw an exception + if ((timeOut > 0) && ((passedTime = (int)DateTime.Now.Subtract(startTime).TotalMilliseconds) >= timeOut)) + { + throw new MDSTimeoutException("All communicators for remote application '" + _remoteApplication.Name + "' are busy. Waited for " + timeOut + " ms."); + } + + //Get a free communicator that can receive message + //TODO: This check is working but not proper for here, move in the future. + receiver = message.DestinationServerName == _remoteApplication.Settings.ThisServerName + ? GetFreeReceiverCommunicator(message.DestinationCommunicatorId) + : GetFreeReceiverCommunicator(0); + + //If no communicator is free, than wait for a free communicator.. + if (receiver == null) + { + if (timeOut > 0) + { + //If there is not free communicator, wait until a communicator is available + Monitor.Wait(_remoteApplication._communicators, timeOut - passedTime); + } + else + { + //If there is not free communicator, wait until a communicator is available + Monitor.Wait(_remoteApplication._communicators); + } + } + else + { + receiver.ProcessingMessage = message; + receiver.ProcessingMessageExpireDate = DateTime.Now.AddMilliseconds(_remoteApplication.Settings.MessageResponseTimeout); + } + } + } + + //Send message to communicator + SetLeavingTime(message); + SendMessage(message, receiver.Communicator); + } + + /// + /// Sends a MDSMessage to a spesific communicator of this application. + /// This method does not block calling thread to wait an ACK for sending message. + /// If communicator is null, then it sends message first communicator of receiver communicators. + /// + /// outgoing message + /// Communicator to send message (may be null) + public void SendMessage(MDSMessage message, ICommunicator communicator) + { + if(communicator == null) + { + lock (_remoteApplication._communicators) + { + var receiverCommunicator = GetAnyReceiverCommunicator(); + //If no receiver is connected to server, throw exception + if (receiverCommunicator == null) + { + throw new MDSNoCommunicatorException("There is no communicator for remote application '" + _remoteApplication.Name + "' to send message."); + } + + communicator = receiverCommunicator.Communicator; + } + } + + communicator.SendMessage(message); + _remoteApplication.LastOutgoingMessageTime = DateTime.Now; + } + + /// + /// This method is used to get MDSOperationResultMessage objects (ACK/Reject messages) by MessageDeliverer class. + /// It is called by MDSRemoteApplication's Communicator_MessageReceived method. + /// + /// Event arguments from Communicator_MessageReceived method + /// True, if message is handled by this method + public bool HandleOperationResultMessage(MessageReceivedFromCommunicatorEventArgs e) + { + lock (_remoteApplication._communicators) + { + var connectedCommunicator = _remoteApplication.FindCommunicator(e.Communicator); + if (connectedCommunicator != null && connectedCommunicator.ProcessingMessage != null && connectedCommunicator.ProcessingMessage.MessageId == e.Message.RepliedMessageId) + { + //Set communicator as free + connectedCommunicator.ProcessingMessage = null; + + //Send receiver to end of the list + _remoteApplication._communicators.Remove(connectedCommunicator); + _remoteApplication._communicators.AddLast(connectedCommunicator); + + //Suspend communicator if it rejected the message + var resultMessage = e.Message as MDSOperationResultMessage; + if (resultMessage != null && !resultMessage.Success) + { + connectedCommunicator.IsSuspended = true; + connectedCommunicator.SuspendExpireDate = DateTime.Now.AddSeconds(15); + } + else + { + //Pulse threads that are waiting for a free communicator + Monitor.PulseAll(_remoteApplication._communicators); + } + + return true; + } + } + + return false; + } + + /// + /// This method is used to know if any receiver exists. + /// + /// True, if at least one receiver connected + public bool IsThereAnyReceiver() + { + foreach (var communicator in _remoteApplication._communicators) + { + if (communicator.Communicator.CommunicationWay == CommunicationWays.SendAndReceive) + { + return true; + } + } + + return false; + } + + #endregion + + /// + /// This method is called by _asynchronMessageControlTimer periodically. + /// See definition of _asynchronMessageControlTimer object. + /// + /// Not used argument + private void AsynchronMessageControlTimer_Elapsed(object state) + { + try + { + //Stop timer during running of this method + _asynchronMessageControlTimer.Change(Timeout.Infinite, Timeout.Infinite); + + lock (_remoteApplication._communicators) + { + //If no communicator do not check anything.. + if (_remoteApplication._communicators.Count <= 0) + { + return; + } + + var pulseCommunicators = false; + var now = DateTime.Now; + foreach (var connectedCommunicator in _remoteApplication._communicators) + { + //Check if receiver is suspended and suspend date expired.. + if (connectedCommunicator.IsSuspended && DateTime.Now.Subtract(connectedCommunicator.SuspendExpireDate).TotalSeconds >= 0) + { + //Reset Suspend flag + connectedCommunicator.IsSuspended = false; + //Pulse threads that are waiting for a free communicator + pulseCommunicators = true; + } + + //If (communicator is not processing any message) OR (processing message is not timed out) then skip communicator.. + if ((connectedCommunicator.ProcessingMessage == null) || (connectedCommunicator.ProcessingMessageExpireDate.Subtract(now).TotalMilliseconds > 0)) + { + continue; + } + + //Send communicator to end of the _receiverCommunicators list. + _remoteApplication._communicators.Remove(connectedCommunicator); + _remoteApplication._communicators.AddLast(connectedCommunicator); + + //Pulse threads that are waiting for a free communicator + pulseCommunicators = true; + + //Send Reject message for processing message, because communicator didn't send result message before ProcessingMessageExpireDate + _remoteApplication.OnResponseReceived( + null, new MDSOperationResultMessage + { + RepliedMessageId = connectedCommunicator.ProcessingMessage.MessageId, + ResultText = "Remote application did not send ACK/Reject. Timeout occured.", + Success = false + }); + connectedCommunicator.ProcessingMessage = null; + } + + //Pulse threads that are waiting for a free communicator. + if(pulseCommunicators) + { + Monitor.PulseAll(_remoteApplication._communicators); + } + } + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + finally + { + //Schedule ping timer for next running if organization layer is still running. + lock (_asynchronMessageControlTimer) + { + if (_remoteApplication._running) + { + _asynchronMessageControlTimer.Change(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); + } + } + } + } + + /// + /// Gets a free Receiver Communicator if exists, else returns null. + /// + /// + /// Communicator's Id to get. + /// If it is smaller or equal to 0, returns any available communicator. + /// + /// Free receiver communicator + private ConnectedCommunicator GetFreeReceiverCommunicator(long communicatorId) + { + var communicatorExists = false; + foreach (var communicator in _remoteApplication._communicators) + { + if ((communicatorId == 0L) || (communicatorId == communicator.Communicator.ComminicatorId)) + { + communicatorExists = true; + } + + //Check if receiver is suspended.. + if ((communicatorId == 0L) && communicator.IsSuspended) + { + //if it is, check if suspend date expired.. + if (DateTime.Now.Subtract(communicator.SuspendExpireDate).TotalSeconds >= 0) + { + //Reset Suspend flag + communicator.IsSuspended = false; + } + else + { + //Skip communicator, it is suspended + continue; + } + } + + //If wants to get a spesific communicator, and this communicator is not that, skip it.. + if ((communicatorId > 0L) && (communicatorId != communicator.Communicator.ComminicatorId)) + { + continue; + } + + //If communicator is not busy and (it is requested communicator or a receiver) than get this communicator. + if ((communicator.ProcessingMessage == null) && ((communicatorId > 0L) || (communicator.Communicator.CommunicationWay == CommunicationWays.SendAndReceive))) + { + return communicator; + } + } + + //If there is no communicator.. + if (!communicatorExists) + { + if (communicatorId > 0) + { + throw new MDSNoCommunicatorException("There is no communicator for remote application '" + + _remoteApplication.Name + "' with communicatorId '" + + communicatorId); + } + + throw new MDSNoCommunicatorException("There is no communicator for remote application '" + + _remoteApplication.Name); + } + + //No available (free) communicator to get + return null; + } + + /// + /// Gets any Receiver Communicator. It does not metter wheter it is busy or free. + /// + /// A receiver communicator + private ConnectedCommunicator GetAnyReceiverCommunicator() + { + foreach (var communicator in _remoteApplication._communicators) + { + if (communicator.Communicator.CommunicationWay == CommunicationWays.SendAndReceive) + { + return communicator; + } + } + + return null; + } + + /// + /// Sets Leaving time for last passed server (this server) of message. + /// + /// Message object + private static void SetLeavingTime(MDSDataTransferMessage message) + { + if (message.PassedServers == null || message.PassedServers.Length <= 0) + { + return; + } + + message.PassedServers[message.PassedServers.Length - 1].LeavingTime = DateTime.Now; + } + } + + #endregion + + #region ConnectedCommunicator class + + /// + /// This class is used to store connected communicators of this applications and their states/activities. + /// + protected class ConnectedCommunicator + { + /// + /// Reference to the communicator object. + /// + public ICommunicator Communicator { get; private set; } + + /// + /// If this communicator is processing a message (that means a message is sent to this communicator and response (ACK/Reject) message is not received yet) + /// this field stores a reference to that message. This indicates that the communicator is busy, so we do not send and message if it is busy. + /// If this field is null that means the communicator is free and can receive messages, so we can send messages to it. + /// + public MDSDataTransferMessage ProcessingMessage { get; set; } + + /// + /// Indicates when the ProcessingMessage is assumed to rejected (not acknowledged) by communicator if any ACK/Reject is received. + /// + public DateTime ProcessingMessageExpireDate { get; set; } + + /// + /// If a communicator rejects a message, it is suspended for a while. + /// This value is true, if communicator is suspended now. + /// + public bool IsSuspended { get; set; } + + /// + /// If IsSuspended is true, this value is the expire date of suspend state. + /// + public DateTime SuspendExpireDate { get; set; } + + /// + /// Creates a new ReceiverCommunicator object. + /// + /// The communicator + public ConnectedCommunicator(ICommunicator communicator) + { + IsSuspended = false; + SuspendExpireDate = DateTime.MinValue; + ProcessingMessageExpireDate = DateTime.MaxValue; + Communicator = communicator; + } + } + + #endregion + } +} diff --git a/src/MDSCore/Communication/MDSRemoteApplication.cs b/src/MDSCore/Communication/MDSRemoteApplication.cs new file mode 100644 index 0000000..95e2291 --- /dev/null +++ b/src/MDSCore/Communication/MDSRemoteApplication.cs @@ -0,0 +1,615 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Settings; +using MDS.Threading; + +namespace MDS.Communication +{ + /// + /// This is a base class for remote application connected to the server as a MDSAdjacentServer, + /// MDSClientApplication or MDSController. + /// + public abstract partial class MDSRemoteApplication : IRunnable + { + #region Events + + /// + /// This event is raised when a message is received from remote application. + /// + public event MessageReceivedFromRemoteApplicationHandler MessageReceived; + + /// + /// This event is raised when a new communicator connection established correctly. + /// + public event CommunicatorConnectedHandler CommunicatorConnected; + + /// + /// This event is raised when a communicator connection closed. + /// + public event CommunicatorDisconnectedHandler CommunicatorDisconnected; + + #endregion + + #region Public properties + + /// + /// Reference to settings. + /// + public MDSSettings Settings { set; protected get; } + + /// + /// An unique ID for this remote application in this server. + /// + public int ApplicationId { get; private set; } + + /// + /// Name of the remote application + /// + public string Name { get; set; } + + /// + /// Communicator/Application Type for this remote application. + /// + public abstract CommunicatorTypes CommunicatorType { get; } + + /// + /// Time of last message received from remote application. + /// + public DateTime LastIncomingMessageTime { get; set; } + + /// + /// Time of last message sent to remote application. + /// + public DateTime LastOutgoingMessageTime { get; set; } + + /// + /// MessageId of last received and acknowledged message's Id. + /// This field is used to do not receive/accept same message again. + /// If a message is send by this remote application with same MessageId, + /// message is discarded and ACK message is sent to application. + /// This field is set and used by OrganizationLayer. (Note: It is better to move this field another class in upper layer, because it is used by upper layer only.) + /// + public string LastAcknowledgedMessageId { get; set; } + + /// + /// Gets connected (online) communicator count for this remote application. + /// + public int ConnectedCommunicatorCount + { + get + { + lock (_communicators) + { + return _communicators.Count; + } + } + } + + #endregion + + #region Protected/Private fields + + /// + /// Reference to logger. + /// + protected static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This field is used to determine maximum allowed communicator count. + /// No more communicator added if communicator count is equal to this number. + /// For infinit communicator, returns -1; + /// + protected virtual int MaxAllowedCommunicatorCount { get { return -1; } } + + /// + /// The connected communication channels associated to this remote application + /// + private readonly LinkedList _communicators; + + /// + /// This queue is used to queue MDSMessage objects received from remote application and process them sequentially. + /// + private readonly QueueProcessorThread _incomingMessageQueue; + + /// + /// This object is used to send/deliver messages to remote applications. + /// + private readonly MessageDeliverer _messageDeliverer; + + /// + /// This flag is used to start/stop MessageDeliverer. + /// + private volatile bool _running; + + #endregion + + #region Constructor + + /// + /// Contructor. + /// + protected MDSRemoteApplication(string name, int applicationId) + { + Name = name; + ApplicationId = applicationId; + LastIncomingMessageTime = DateTime.MinValue; + LastOutgoingMessageTime = DateTime.MinValue; + _communicators = new LinkedList(); + _messageDeliverer = new MessageDeliverer(this); + _incomingMessageQueue = new QueueProcessorThread(); + _incomingMessageQueue.ProcessItem += IncomingMessageQueue_ProcessItem; + } + + #endregion + + #region Public methods + + #region Starting / Stopping methods + + /// + /// Starts processing of incoming messages. + /// + public virtual void Start() + { + _running = true; + StartCommunicators(); + _messageDeliverer.Start(); + _incomingMessageQueue.Start(); + } + + /// + /// Stops processing incoming messages + /// + /// True, to wait until stopped + public virtual void Stop(bool waitToStop) + { + _running = false; + + //Stop processing new incoming messages + _incomingMessageQueue.Stop(waitToStop); + + //Stop communicator objects + StopCommunicators(); + + //Pulses WaitUntilReceiverCommunicatorConnected (waiting for a communicator) + //and MessageDeliverer.SendDataMessage (waiting for available receiver) methods. + //They must no wait anymore, since MDS is shutting down. + lock (_communicators) + { + Monitor.PulseAll(_communicators); + } + + //Stop message deliverer + _messageDeliverer.Stop(waitToStop); + } + + /// + /// Waits until this object is stopped correctly. + /// + public virtual void WaitToStop() + { + _incomingMessageQueue.WaitToStop(); + _messageDeliverer.WaitToStop(); + } + + #endregion + + #region Communicator methods + + /// + /// Adds a new communication channel for this application. + /// + /// new communicator + public void AddCommunicator(ICommunicator communicator) + { + lock (_communicators) + { + //Check if communicator count reached to maximum + var maxAllowedCommunicatorCount = MaxAllowedCommunicatorCount; + if((maxAllowedCommunicatorCount >=0) && (_communicators.Count >= maxAllowedCommunicatorCount)) + { + throw new MDSException("New communicator connection is not allowed, because Communicator count reached to maximum communicator count (" + maxAllowedCommunicatorCount + ")"); + } + + var connectedCommunicator = FindCommunicator(communicator); + if (connectedCommunicator != null) + { + return; + } + + //Add to list + _communicators.AddLast(new ConnectedCommunicator(communicator)); + + //Update incoming/outgoing message dates + LastIncomingMessageTime = DateTime.Now; + LastOutgoingMessageTime = DateTime.Now; + + //Register to events of communicator + communicator.MessageReceived += Communicator_MessageReceived; + communicator.StateChanged += Communicator_StateChanged; + + //Pulse threads that are waiting for a communicator + Monitor.PulseAll(_communicators); + + Logger.Info("Connection established with Remote Application: " + Name); + } + + OnCommunicatorConnected(communicator); + } + + /// + /// Searches throught _communicators list and checks if it contains a record with a spesified communicator object. + /// If it contains, returns the ConnectedCommunicator object, else returns null. + /// + /// Communicator object to search + /// ConnectedCommunicator object, if _communicators contains communicator object + private ConnectedCommunicator FindCommunicator(ICommunicator communicator) + { + lock (_communicators) + { + foreach (var communicatorItem in _communicators) + { + if (communicatorItem.Communicator == communicator) + { + return communicatorItem; + } + } + } + + return null; + } + + #endregion + + #region Message sending methods + + /// + /// This method is used to send a MDSDataTransferMessage to an available communicator of application. + /// It just blocks caller thread until a communicator comes available and message is sent or until timeout, + /// but receives result (ACK/Reject) message asynchronous. It sends result (ACK/Reject) message to OnResponseReceived() method. + /// + /// Message to send + /// Timeout value to wait if all receivers are busy. Set 0 to do not use timeout. + public void SendDataMessage(MDSDataTransferMessage message, int timeOut) + { + _messageDeliverer.SendDataMessage(message, timeOut); + } + + /// + /// Sends a MDSMessage to this application. + /// This method does not block calling thread to wait an ACK for sending message. + /// This method is just an overload for SendMessage(MDSMessage, ICommunicator) method as communicator is null. + /// + /// outgoing message + public void SendMessage(MDSMessage message) + { + SendMessage(message, null); + } + + /// + /// Sends a MDSMessage to a spesific communicator of this application. + /// This method does not block calling thread to wait an ACK for sending message. + /// If communicator is null, then it sends message first communicator of receiver communicators. + /// + /// outgoing message + /// Communicator to send message (may be null) + public void SendMessage(MDSMessage message, ICommunicator communicator) + { + _messageDeliverer.SendMessage(message, communicator); + } + + #endregion + + #endregion + + #region Protected methods + + /// + /// This method is used to wait a thread until a receiver communicatior connection established. + /// + protected void WaitUntilReceiverCommunicatorConnected() + { + lock (_communicators) + { + if (_running && (!_messageDeliverer.IsThereAnyReceiver())) + { + Monitor.Wait(_communicators); + } + } + } + + /// + /// Checks if there is any connected communicator exists (at least 1 connected communicator). + /// + /// True, if there is one communicator at least + protected bool IsThereCommunicator() + { + lock (_communicators) + { + return (_communicators.Count > 0); + } + } + + /// + /// Gets a list of all connected receiver communicators. + /// + /// All receiver communicators + protected List GetAllReceiverCommunicators() + { + var receivers = new List(); + lock (_communicators) + { + foreach (var connectedCommunicator in _communicators) + { + if (connectedCommunicator.Communicator.CommunicationWay == CommunicationWays.SendAndReceive) + { + receivers.Add(connectedCommunicator.Communicator); + } + } + } + + return receivers; + } + + #region Virtual methods + + protected virtual void OnResponseReceived(ICommunicator communicator, MDSOperationResultMessage operationResultMessage) + { + //No action + } + + #endregion + + #endregion + + #region Private methods + + /// + /// When a communicator's state is changed, this method handles event.. + /// + /// Creator of event + /// Event arguments + private void Communicator_StateChanged(object sender, CommunicatorStateChangedEventArgs e) + { + //Process only CommunicationStates.Closed state + if (e.Communicator.State != CommunicationStates.Closed) + { + return; + } + + lock (_communicators) + { + var connectedCommunicator = FindCommunicator(e.Communicator); + if (connectedCommunicator == null) + { + return; + } + + _communicators.Remove(connectedCommunicator); + + //Send Reject message for processing message, because communicator is disconnected + if (connectedCommunicator.ProcessingMessage != null) + { + OnResponseReceived( + null, + new MDSOperationResultMessage + { + RepliedMessageId = connectedCommunicator.ProcessingMessage.MessageId, + Success = false, + ResultText = "Communicator of remote application disconnected from server." + }); + } + + Logger.Info("A connection closed with remote application: " + Name); + } + + OnCommunicatorDisconnected(e.Communicator); + } + + /// + /// When a communicator is received a message, this method handles event.. + /// + /// Creator of event + /// Event arguments + private void Communicator_MessageReceived(object sender, MessageReceivedFromCommunicatorEventArgs e) + { + //Update last incoming message time + LastIncomingMessageTime = DateTime.Now; + + //Check if this is an ACK/Reject message for a data transfer message + if ((e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSOperationResultMessage) && + (!string.IsNullOrEmpty(e.Message.RepliedMessageId))) + { + ProcessOperationResultMessage(e); + return; + } + + //Check if this is an MDSChangeCommunicationWayMessage + if (e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSChangeCommunicationWayMessage) + { + ProcessChangeCommunicationWayMessage(e); + return; + } + + //Add message to incoming message queue to process as ordered + _incomingMessageQueue.Add(e); + } + + /// + /// Processes a MDSOperationResultMessage message. + /// + /// Event arguments from Communicator_MessageReceived method + private void ProcessOperationResultMessage(MessageReceivedFromCommunicatorEventArgs e) + { + //Send message to message deliverer to process + var handled = _messageDeliverer.HandleOperationResultMessage(e); + /* If message is handled, OnResponseReceived event is raised, + * thus, caller of SendDataMessage (MDSPersistentRemoteApplicationBase.ProcessWaitingMessage) method + * gets response. */ + if (handled) + { + OnResponseReceived(e.Communicator, e.Message as MDSOperationResultMessage); + } + } + + /// + /// Processes a MDSChangeCommunicationWayMessage message. + /// + /// Event arguments from Communicator_MessageReceived method + private static void ProcessChangeCommunicationWayMessage(MessageReceivedFromCommunicatorEventArgs e) + { + var message = e.Message as MDSChangeCommunicationWayMessage; + if (message == null) + { + return; + } + + //Change communication way + e.Communicator.CommunicationWay = message.NewCommunicationWay; + } + + /// + /// This method is called for each incoming message by _incomingMessageQueue to process incoming messages as ordered. + /// + /// Sender object + /// Event arguments + private void IncomingMessageQueue_ProcessItem(object sender, ProcessQueueItemEventArgs e) + { + OnMessageReceived(this, e.ProcessItem); + } + + /// + /// This method is used to start static communicators (like Web Service communicators). + /// + private void StartCommunicators() + { + foreach (var connectedCommunicator in _communicators) + { + connectedCommunicator.Communicator.Start(); + } + } + + /// + /// This method is used to stop communicators that are not first connected to this server but this server opened connection to them. + /// It also stops web service communicators. + /// Because they are not contained in Communication layer, connection between them is closed by this method. + /// + private void StopCommunicators() + { + while (_communicators.Count > 0) + { + try + { + _communicators.First.Value.Communicator.Stop(true); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + } + + #region Event raising methods + + /// + /// Raises a MessageReceived event. + /// + /// Creator of event + /// Event arguments + protected void OnMessageReceived(object sender, MessageReceivedFromCommunicatorEventArgs e) + { + try + { + if (MessageReceived != null) + { + MessageReceived(sender, new MessageReceivedFromRemoteApplicationEventArgs + { + Application = this, + Communicator = e.Communicator, + Message = e.Message + }); + } + else + { + //Disconnect communicator if there is not listener for incoming messages from this communicator. + e.Communicator.Stop(false); + } + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + /// + /// Raises a CommunicatorConnected event. + /// + /// New connected communicator + private void OnCommunicatorConnected(ICommunicator communicator) + { + if (CommunicatorConnected == null) + { + return; + } + + try + { + CommunicatorConnected(this, new CommunicatorConnectedEventArgs { Communicator = communicator }); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + /// + /// Raises a CommunicatorConnected event. + /// + /// New connected communicator + private void OnCommunicatorDisconnected(ICommunicator communicator) + { + if (CommunicatorConnected == null) + { + return; + } + + try + { + CommunicatorDisconnected(this, new CommunicatorDisconnectedEventArgs {Communicator = communicator}); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #endregion + } +} diff --git a/src/MDSCore/Communication/TCPCommunication/TCPClientConnectedEventArgs.cs b/src/MDSCore/Communication/TCPCommunication/TCPClientConnectedEventArgs.cs new file mode 100644 index 0000000..24757da --- /dev/null +++ b/src/MDSCore/Communication/TCPCommunication/TCPClientConnectedEventArgs.cs @@ -0,0 +1,42 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Net.Sockets; + +namespace MDS.Communication.TCPCommunication +{ + /// + /// A delegate to create events for connected TCP clients to this server. + /// + /// The object which raises event + /// Event arguments + public delegate void TCPClientConnectedHandler(object sender, TCPClientConnectedEventArgs e); + + /// + /// Stores informations about connected client. + /// + public class TCPClientConnectedEventArgs : EventArgs + { + /// + /// Client Socket Connection + /// + public Socket ClientSocket { get; set; } + } +} diff --git a/src/MDSCore/Communication/TCPCommunication/TCPCommunicationManager.cs b/src/MDSCore/Communication/TCPCommunication/TCPCommunicationManager.cs new file mode 100644 index 0000000..773de13 --- /dev/null +++ b/src/MDSCore/Communication/TCPCommunication/TCPCommunicationManager.cs @@ -0,0 +1,112 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using log4net; + +namespace MDS.Communication.TCPCommunication +{ + /// + /// A communication manager that listens and handles incoming connections and messages using TCP. + /// + public class TCPCommunicationManager : CommunicationManagerBase + { + #region Private fields + + /// + /// Reference to logger + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The object to listen and handle incoming connection requests. + /// + private readonly TCPConnectionListener _connectionListener; + + #endregion + + #region Constructor + + /// + /// Constructor. + /// + /// Server's listening TCP port number + public TCPCommunicationManager(int port) + { + _connectionListener = new TCPConnectionListener(port); + _connectionListener.TCPClientConnected += ConnectionListener_ClientConnected; + } + + #endregion + + #region Public methods + + /// + /// Starts listening of port and handling connections. + /// + public override void Start() + { + _connectionListener.Start(); + } + + /// + /// Stops listening of port and handling connections. + /// Closes all open communicator connection. + /// + /// Indicates that caller thread waits stopping of manager + public override void Stop(bool waitToStop) + { + _connectionListener.Stop(waitToStop); + } + + /// + /// Waits stopping of communication manager. + /// + public override void WaitToStop() + { + _connectionListener.WaitToStop(); + } + + #endregion + + #region Private methods + + /// + /// When TCPConnectionListener handles a connection, it is taken by this method to create and + /// register it's neccessary events and to add it to _communicators collection. + /// This method is also starts the communicator. + /// + /// Sending object + /// Event informations + private void ConnectionListener_ClientConnected(object sender, TCPClientConnectedEventArgs e) + { + try + { + OnCommunicatorConnected(new TCPCommunicator(e.ClientSocket, CommunicationLayer.CreateCommunicatorId())); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + #endregion + } +} diff --git a/src/MDSCore/Communication/TCPCommunication/TCPCommunicator.cs b/src/MDSCore/Communication/TCPCommunication/TCPCommunicator.cs new file mode 100644 index 0000000..0cbc9ee --- /dev/null +++ b/src/MDSCore/Communication/TCPCommunication/TCPCommunicator.cs @@ -0,0 +1,242 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using log4net; +using MDS.Communication.Messages; +using MDS.Communication.Protocols; +using MDS.Exceptions; +using MDS.Serialization; + +namespace MDS.Communication.TCPCommunication +{ + /// + /// This class represents an communication channel with a Remote Application via TCP sockets. + /// + public class TCPCommunicator : CommunicatorBase + { + #region Private fields + + /// + /// Reference to logger + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The TCP socket to the remote application. + /// + private readonly Socket _socket; + + /// + /// The main stream wraps socket to send/receive data. + /// + private NetworkStream _networkStream; + + /// + /// This object is used to send/receive messages as byte array. + /// + private readonly IMDSWireProtocol _wireProtocol; + + /// + /// The thread that listens incoming data. + /// + private Thread _thread; + + #endregion + + #region Constructors + + /// + /// Constructor. + /// + /// Open TCP socket connection to the communicator. + /// Unique identifier for this communicator. + public TCPCommunicator(Socket socket, long comminicatorId) + : base(comminicatorId) + { + _socket = socket; + _socket.NoDelay = true; + CommunicationWay = CommunicationWays.SendAndReceive; + _wireProtocol = new MDSDefaultWireProtocol(); + } + + #endregion + + #region Public methods + + /// + /// Waits communicator to stop. + /// + public override void WaitToStop() + { + if (_thread == null) + { + return; + } + + try + { + _thread.Join(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #region Protected methods + + /// + /// Prepares communication objects and starts data listening thread. + /// + protected override void StartCommunicaiton() + { + if (!_socket.Connected) + { + throw new MDSException("Tried to start communication with a TCP socket that is not connected."); + } + + _networkStream = new NetworkStream(_socket); + + _thread = new Thread(DoCommunicateAsThread); + _thread.Start(); + } + + /// + /// Closes the socket and stops the thread. + /// + /// True, to block caller thread until this object stops + protected override void StopCommunicaiton(bool waitToStop) + { + if (_socket.Connected) + { + _socket.Shutdown(SocketShutdown.Send); + _socket.Close(); + } + + if (waitToStop) + { + WaitToStop(); + } + } + + /// + /// Sends a message to the TCP communicator according Communication type + /// + /// Message to send + protected override void SendMessageInternal(MDSMessage message) + { + if(State != CommunicationStates.Connected) + { + throw new MDSException("Communicator's state is not connected. It can not send message."); + } + + SendMessageToSocket(message); + } + + #endregion + + #region Private methods + + /// + /// Entrance point of the thread. + /// This method run by thread to listen incoming data from communicator. + /// + private void DoCommunicateAsThread() + { + Logger.Debug("TCPCommunicator thread is started. CommunicatorId=" + ComminicatorId); + + while (State == CommunicationStates.Connected || State == CommunicationStates.Connecting) + { + try + { + //Read a message from _networkStream (socket) and raise MessageReceived event + var message = _wireProtocol.ReadMessage(new MDSDefaultDeserializer(_networkStream)); + Logger.Debug("Message received by communicator " + ComminicatorId + ": " + message.GetType().Name); + OnMessageReceived(message); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + break; //Stop listening + } + } + + //if socket is still connected, then close it + try + { + Stop(false); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + + Logger.Debug("TCPCommunicator is stopped. CommunicatorId=" + ComminicatorId); + + _thread = null; + } + + /// + /// Sends MDSMessage object to the socket. + /// + /// Message to be sent + private void SendMessageToSocket(MDSMessage message) + { + Logger.Debug("Message is being sent to communicator " + ComminicatorId + ": " + message.GetType().Name); + + //Create MemoryStream to write message to a byte array + var memoryStream = new MemoryStream(); + + //Write message + _wireProtocol.WriteMessage(new MDSDefaultSerializer(memoryStream), message); + + //Check the length of message data + if (memoryStream.Length > CommunicationConsts.MaxMessageSize) + { + throw new Exception("Message is too big to send."); + } + + //SendMessage message (contents of created memory stream) + var sendBuffer = memoryStream.ToArray(); + var length = sendBuffer.Length; + var totalSent = 0; + while (totalSent < length) + { + var sent = _socket.Send(sendBuffer, totalSent, length - totalSent, SocketFlags.None); + if (sent <= 0) + { + throw new Exception("Message can not be sent via TCP socket. Only " + totalSent + " bytes of " + length + " bytes are sent."); + } + + totalSent += sent; + } + + Logger.Debug("Message is sent to communicator " + ComminicatorId + ": " + message.GetType().Name); + } + + #endregion + } +} diff --git a/src/MDSCore/Communication/TCPCommunication/TCPConnectionListener.cs b/src/MDSCore/Communication/TCPCommunication/TCPConnectionListener.cs new file mode 100644 index 0000000..b0d0c17 --- /dev/null +++ b/src/MDSCore/Communication/TCPCommunication/TCPConnectionListener.cs @@ -0,0 +1,219 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using System.Threading; +using System.Net.Sockets; +using log4net; +using MDS.Threading; + +namespace MDS.Communication.TCPCommunication +{ + /// + /// This class is used to listen and accept incoming TCP connection requests on given TCP port. + /// + public class TCPConnectionListener : IRunnable + { + #region Events + + /// + /// When a client successfully connected the server, this event is raised. + /// + public event TCPClientConnectedHandler TCPClientConnected; + + #endregion + + #region Public properties + + /// + /// Listening port number + /// + public int Port { get; private set; } + + #endregion + + #region Private fields + + /// + /// Reference to logger + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Server socket to listen incoming connection requests. + /// + private TcpListener _listenerSocket; + + /// + /// The thread to listen socket + /// + private volatile Thread _thread; + + /// + /// A flag to control thread's running + /// + private volatile bool _running; + + #endregion + + #region Constructors + + /// + /// Creates a new TCPConnectionListener with given tcp port. + /// + /// Listening TCP port no + public TCPConnectionListener(int port) + { + Port = port; + } + + #endregion + + #region Public methods + + /// + /// Starts listening. + /// + public void Start() + { + StartSocket(); + _running = true; + _thread = new Thread(DoListenAsThread); + _thread.Start(); + } + + /// + /// Stops listening. + /// + /// True, if caller method must wait until running stops. + public void Stop(bool waitToStop) + { + _running = false; + StopSocket(); + if (waitToStop) + { + WaitToStop(); + } + } + + /// + /// Waits until listener finishes it's work, if it is working. + /// + public void WaitToStop() + { + if (_thread == null) + { + return; + } + + try + { + _thread.Join(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #region Private methods + + /// + /// Starts listening socket. + /// + private void StartSocket() + { + _listenerSocket = new TcpListener(System.Net.IPAddress.Any, Port); + _listenerSocket.Start(); + } + + /// + /// Stops listening socket. + /// + /// Indicates the result of operation + private void StopSocket() + { + _listenerSocket.Stop(); + } + + /// + /// Entrance point of the thread. + /// This method is used by the thread to listen incoming requests. + /// + private void DoListenAsThread() + { + while (_running) + { + try + { + //Wait and get connected socket + var clientSocket = _listenerSocket.AcceptSocket(); + + //Raise TCPClientConnected event + if (clientSocket.Connected && (TCPClientConnected != null)) + { + TCPClientConnected(this, new TCPClientConnectedEventArgs {ClientSocket = clientSocket}); + } + } + catch (Exception ex) + { + if (!_running) + { + return; + } + + //on an exception, close connection, wait for a while and start again + Logger.Error(ex.Message, ex); + + try + { + StopSocket(); + } + catch (Exception exStop) + { + Logger.Error(exStop.Message, exStop); + } + + Thread.Sleep(3000); //Wait 3 seconds + + if (!_running) + { + return; + } + + try + { + StartSocket(); + } + catch (Exception exStart) + { + Logger.Error(exStart.Message, exStart); + } + } + } + + _thread = null; + } + + #endregion + } +} diff --git a/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicationManager.cs b/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicationManager.cs new file mode 100644 index 0000000..6a714b4 --- /dev/null +++ b/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicationManager.cs @@ -0,0 +1,38 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Communication.WebServiceCommunication +{ + public class WebServiceCommunicationManager : CommunicationManagerBase + { + public override void Start() + { + + } + + public override void Stop(bool waitToStop) + { + + } + + public override void WaitToStop() + { + } + } +} diff --git a/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicator.cs b/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicator.cs new file mode 100644 index 0000000..eea02e9 --- /dev/null +++ b/src/MDSCore/Communication/WebServiceCommunication/WebServiceCommunicator.cs @@ -0,0 +1,150 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using log4net; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.MDSAppWebServiceRef; +using MDS.Serialization; +using MDS.Threading; + +namespace MDS.Communication.WebServiceCommunication +{ + /// + /// This class is used to communicate with a ASP.NET Web Service. + /// + public class WebServiceCommunicator : CommunicatorBase + { + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// URL of web service. + /// + private readonly string _url; + + /// + /// This queue is used to make web service calls sequentially. + /// + private readonly QueueProcessorThread _outgoingMessageQueue; + + /// + /// Creates a new WebServiceCommunicator object. + /// + /// URL of web service + /// Communicator Id + public WebServiceCommunicator(string url, long comminicatorId) + : base(comminicatorId) + { + _url = url; + CommunicationWay = CommunicationWays.SendAndReceive; + _outgoingMessageQueue = new QueueProcessorThread(); + _outgoingMessageQueue.ProcessItem += OutgoingMessageQueue_ProcessItem; + } + + /// + /// Waits communicator to stop. + /// + public override void WaitToStop() + { + _outgoingMessageQueue.WaitToStop(); + } + + /// + /// Prepares communication objects and starts outgoing message queue. + /// + protected override void StartCommunicaiton() + { + _outgoingMessageQueue.Start(); + } + + /// + /// Starts outgoing message queue. + /// + /// True, to block caller thread until this object stops + protected override void StopCommunicaiton(bool waitToStop) + { + _outgoingMessageQueue.Stop(waitToStop); + } + + /// + /// This method is used to add a message to outgoing messages queue. + /// It is called by CommunicatorBase. + /// + /// Message to send + protected override void SendMessageInternal(MDSMessage message) + { + if (message.MessageTypeId != MDSMessageFactory.MessageTypeIdMDSDataTransferMessage) + { + return; + } + + _outgoingMessageQueue.Add(message as MDSDataTransferMessage); + } + + /// + /// This method is called to process a outgoing message. + /// + /// Sender object + /// Event arguments + private void OutgoingMessageQueue_ProcessItem(object sender, ProcessQueueItemEventArgs e) + { + try + { + SendMessageToWebService(e.ProcessItem); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// Makes web service call, receives result and raises MessageReceived event. + /// + /// + private void SendMessageToWebService(MDSDataTransferMessage message) + { + using (var appService = new MDSAppService()) + { + appService.Url = _url; + var responseMessageBytes = appService.ReceiveMDSMessage(MDSSerializationHelper.SerializeToByteArray(message)); + if (responseMessageBytes == null) + { + throw new MDSException("Response byte array from web service call is null."); + } + + var responseMessage = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferResponseMessage(), responseMessageBytes); + if (responseMessage.Result != null) + { + OnMessageReceived(responseMessage.Result); + } + + if (responseMessage.Message != null) + { + OnMessageReceived(responseMessage.Message); + } + } + } + } +} diff --git a/src/MDSCore/Liscense.txt b/src/MDSCore/Liscense.txt new file mode 100644 index 0000000..83a488d --- /dev/null +++ b/src/MDSCore/Liscense.txt @@ -0,0 +1,16 @@ +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/MDSCore/MDSCore.csproj b/src/MDSCore/MDSCore.csproj new file mode 100644 index 0000000..999e9c6 --- /dev/null +++ b/src/MDSCore/MDSCore.csproj @@ -0,0 +1,181 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AB59B8B2-D414-483F-AA72-A2644D92C86D} + Library + Properties + MDS + MDSCore + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\Libraries\log4net.dll + + + False + ..\Dependencies\Libraries\MySql.Data.dll + + + + 3.5 + + + False + ..\Dependencies\Libraries\System.Data.SQLite.dll + + + + 3.0 + + + 3.0 + + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + + + + + + + + + + + + + True + True + Reference.map + + + + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + MSDiscoCodeGenerator + Reference.cs + + + + + {055149D1-A267-4E2E-B8AE-EA7848A45701} + MDSCommonLib + + + + + + + + + + + Dynamic + Web References\MDSAppWebServiceRef\ + http://localhost:2111/MDSWebServiceInterface/MDSAppService.asmx + + + + + Settings + MDSCore_MDSAppWebServiceRef_MDSAppService + + + + + \ No newline at end of file diff --git a/src/MDSCore/MDSServer.cs b/src/MDSCore/MDSServer.cs new file mode 100644 index 0000000..a5c90aa --- /dev/null +++ b/src/MDSCore/MDSServer.cs @@ -0,0 +1,140 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication; +using MDS.Organization; +using MDS.Organization.Routing; +using MDS.Settings; +using MDS.Storage; +using MDS.Threading; + +namespace MDS +{ + /// + /// Represents a MDS server. This is the main class to construct and run a MDS server. + /// + public class MDSServer : IRunnable + { + /// + /// Settings. + /// + private readonly MDSSettings _settings; + + /// + /// Storage manager used for database operations. + /// + private readonly IStorageManager _storageManager; + + /// + /// Routing table. + /// + private readonly RoutingTable _routingTable; + + /// + /// A Graph consist of server nodes. It also holds references to MDSAdjacentServer objects. + /// + private readonly MDSServerGraph _serverGraph; + + /// + /// Reference to all MDS Managers. It contains communicators to all instances of MDS manager. + /// + private readonly MDSController _mdsManager; + + /// + /// List of applications + /// + private readonly MDSClientApplicationList _clientApplicationList; + + /// + /// Communication layer. + /// + private readonly CommunicationLayer _communicationLayer; + + /// + /// Organization layer. + /// + private readonly OrganizationLayer _organizationLayer; + + /// + /// Constructor. + /// + public MDSServer() + { + _settings = MDSSettings.Instance; + _serverGraph = new MDSServerGraph(); + _clientApplicationList = new MDSClientApplicationList(); + _mdsManager = new MDSController("MDSController"); + _storageManager = StorageManagerFactory.CreateStorageManager(); + _routingTable = new RoutingTable(); + _communicationLayer = new CommunicationLayer(); + _organizationLayer = new OrganizationLayer(_communicationLayer, _storageManager, _routingTable, _serverGraph, _clientApplicationList, _mdsManager); + _mdsManager.OrganizationLayer = _organizationLayer; + } + + /// + /// Starts the MDS server. + /// + public void Start() + { + _storageManager.Start(); + CorrectDatabase(); + _communicationLayer.Start(); + _organizationLayer.Start(); + } + + /// + /// Stops the MDS server. + /// + /// True, if caller thread must be blocked until MDS server stops. + public void Stop(bool waitToStop) + { + _communicationLayer.Stop(waitToStop); + _organizationLayer.Stop(waitToStop); + _storageManager.Stop(waitToStop); + } + + /// + /// Waits stopping of MDS server. + /// + public void WaitToStop() + { + _communicationLayer.WaitToStop(); + _organizationLayer.WaitToStop(); + _storageManager.WaitToStop(); + } + + /// + /// Checks and corrects database records if needed. + /// + private void CorrectDatabase() + { + if (_settings["CheckDatabaseOnStartup"] != "true") + { + return; + } + + //If Server graph is changed, records in storage engine (database) may be wrong, therefore, they must be updated + var nextServersList = _serverGraph.GetNextServersForDestServers(); + foreach (var nextServerItem in nextServersList) + { + _storageManager.UpdateNextServer(nextServerItem.Key, nextServerItem.Value); + } + } + } +} diff --git a/src/MDSCore/Organization/MDSAdjacentServer.cs b/src/MDSCore/Organization/MDSAdjacentServer.cs new file mode 100644 index 0000000..a224325 --- /dev/null +++ b/src/MDSCore/Organization/MDSAdjacentServer.cs @@ -0,0 +1,322 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Net; +using MDS.Communication; +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Communication.TCPCommunication; +using MDS.Exceptions; +using MDS.Storage; + +namespace MDS.Organization +{ + /// + /// An MDSAsjacentServer is a server in MDSServerGraph that is an adjacent of this server + /// and directly communicates with this server. + /// + public class MDSAdjacentServer : MDSPersistentRemoteApplicationBase + { + #region Public properties + + /// + /// IP Address of Server on network + /// + public string IpAddress + { + get { return _ipAddress; } + } + private readonly string _ipAddress; + + /// + /// Listening port number of MDS on Server. + /// + public int Port + { + get { return _port; } + } + private readonly int _port; + + /// + /// Communicator Type for MDS server + /// + public override CommunicatorTypes CommunicatorType + { + get { return CommunicatorTypes.MdsServer; } + } + + /// + /// This field is used to determine maximum allowed communicator count. + /// No more communicator added if communicator count is equal to this number. + /// For infinit communicator, returns -1; + /// + /// Only 1 communicator is allowed in any time for a MDSAdjacentServer. + /// + protected override int MaxAllowedCommunicatorCount + { + get { return 1; } + } + + #endregion + + #region Private fields + + /// + /// The time last connection attempt to remote MDS server. + /// + private DateTime _lastConnectionAttemptTime; + + /// + /// Consecutive error count on trying to connect to remote MDS server. + /// + private int _connectionErrorCount; + + /// + /// This communicator object is temporary used to reconnect to remote MDS server. + /// After connection it is added to communicators list and set to null. + /// + private TCPCommunicator _reconnectingCommunicator; + + #endregion + + #region Constructors + + /// + /// Constructur. + /// + /// name of server + /// IP Address of server + /// Listening TCP port of server + public MDSAdjacentServer(string name, string ipAddress, int port) + : base(name) + { + _ipAddress = ipAddress; + _port = port; + _lastConnectionAttemptTime = DateTime.MinValue; + } + + #endregion + + #region Public methods + + /// + /// This method is responsible to ensure connection with communicating MDS server. + /// Checks connection, reconnects if disconnected and sends ping message. + /// + public void CheckConnection() + { + try + { + if (IsThereCommunicator()) + { + SendPingMessageIfNeeded(); + } + + CheckConnectionAndReConnectIfNeeded(); + } + catch (Exception ex) + { + Logger.Warn("Can not connected to MDS Server: " + Name); + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #region Protected / Overrive methods + + /// + /// Gets messages from database to be sent to this server. + /// + /// Minimum Id of message record to get (minId included) + /// Maximum number of records to get + /// List of messages + protected override List GetWaitingMessages(int minId, int maxCount) + { + return StorageManager.GetWaitingMessagesOfServer(Name, minId, maxCount); + } + + /// + /// Gets Id of last incoming message that will be sent to this server. + /// + /// Id of last incoming message + protected override int GetMaxWaitingMessageId() + { + return StorageManager.GetMaxWaitingMessageIdOfServer(Name); + } + + /// + /// Finds Next server for a message. + /// + /// Next server + protected override string GetNextServerForMessage(MDSDataTransferMessage message) + { + //Next server is this MDSAdjacentServer because message is being sent to that now. + return Name; + } + + #endregion + + #region Private methods + + /// + /// Checks connection and reconnects to MDS server if needed. + /// + private void CheckConnectionAndReConnectIfNeeded() + { + var now = DateTime.Now; + + if (IsThereCommunicator()) + { + // If any (dublex) communication made within last 90 seconds, then do not create connection. + if (now.Subtract(LastIncomingMessageTime).TotalSeconds <= 90 && + now.Subtract(LastOutgoingMessageTime).TotalSeconds <= 90) + { + return; + } + } + + //Wait 1 second more on every failed connection attempt (Maximum 30 seconds). + var waitSeconds = Math.Min(++_connectionErrorCount, 30); + if (now.Subtract(_lastConnectionAttemptTime).TotalSeconds < waitSeconds) + { + return; + } + + Logger.Info("Connecting remote MDS Server: " + Name + " (Attempt: " + _connectionErrorCount + ")"); + _lastConnectionAttemptTime = DateTime.Now; + ConnectToServer(); + _connectionErrorCount = 0; + } + + /// + /// Sends a Ping message to MDS server if 60 seconds passed after last communication. + /// + private void SendPingMessageIfNeeded() + { + var now = DateTime.Now; + if (now.Subtract(LastIncomingMessageTime).TotalSeconds < 60 && + now.Subtract(LastOutgoingMessageTime).TotalSeconds < 60) + { + return; + } + + try + { + SendMessage(new MDSPingMessage()); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + /// + /// Creates a new TCPCommunicator to communicate with MDS server. + /// + private void ConnectToServer() + { + var ip = IPAddress.Parse(_ipAddress); + if (ip == null) + { + throw new MDSException("IP address is not valid: " + _ipAddress); + } + + var socket = GeneralHelper.ConnectToServerWithTimeout(new IPEndPoint(ip, _port), 10000); //10 seconds + if (!socket.Connected) + { + throw new MDSException("TCP connection can not be established."); + } + + //Create communicator object. + _reconnectingCommunicator = new TCPCommunicator(socket, CommunicationLayer.CreateCommunicatorId()); + + //Register MessageReceived event to receive response of Register message + _reconnectingCommunicator.MessageReceived += Communicator_MessageReceived; + + //Start communicator and send a register message + _reconnectingCommunicator.Start(); + _reconnectingCommunicator.SendMessage(new MDSRegisterMessage + { + CommunicationWay = CommunicationWays.SendAndReceive, + CommunicatorType = CommunicatorTypes.MdsServer, + Name = Settings.ThisServerName, + Password = "" //Not implemented yet + }); + } + + /// + /// This method is just used to make a new connection with MDS server. + /// It receives response of register message and adds communicator to Communicators if successfuly registered. + /// + /// Creator object of event + /// Event arguments + private void Communicator_MessageReceived(object sender, MessageReceivedFromCommunicatorEventArgs e) + { + try + { + //Message must be MDSOperationResultMessage + var message = e.Message as MDSOperationResultMessage; + if (message == null) + { + throw new MDSException("First message must be MDSOperationResultMessage"); + } + + //Check if remote MDS server accepted connection + if(!message.Success) + { + throw new MDSException("Remote MDS server did not accept connection."); + } + + //Unregister from event and add communicator to Communicators list. + e.Communicator.MessageReceived -= Communicator_MessageReceived; + try + { + AddCommunicator(e.Communicator); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + e.Communicator.Stop(false); + } + } + catch (Exception ex) + { + Logger.Warn("Can not connected to remote MDS server: '" + Name + "'. Connection is being closed."); + Logger.Warn(ex.Message, ex); + try + { + e.Communicator.Stop(false); + } + catch (Exception ex2) + { + Logger.Warn(ex2.Message, ex2); + } + } + finally + { + _reconnectingCommunicator = null; + } + } + + #endregion + } +} diff --git a/src/MDSCore/Organization/MDSClientApplication.cs b/src/MDSCore/Organization/MDSClientApplication.cs new file mode 100644 index 0000000..5c1ddbb --- /dev/null +++ b/src/MDSCore/Organization/MDSClientApplication.cs @@ -0,0 +1,92 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; +using MDS.Communication; +using MDS.Communication.Messages; +using MDS.Storage; + +namespace MDS.Organization +{ + /// + /// Represents a Client Application that can send and received messages to/from this server. + /// + public class MDSClientApplication : MDSPersistentRemoteApplicationBase + { + #region Public properties + + /// + /// Communicator type for Client applications. + /// + public override CommunicatorTypes CommunicatorType + { + get { return CommunicatorTypes.Application; } + } + + #endregion + + #region Constructors + + /// + /// Creates a new MDSClientApplication. + /// + /// Unique name of the application + public MDSClientApplication(string name) + : base(name) + { + //No action + } + + #endregion + + #region Protected / Overrive methods + + /// + /// Gets messages from database to be sent to this application. + /// + /// Minimum Id of message record to get (minId included) + /// Maximum number of records to get + /// List of messages + protected override List GetWaitingMessages(int minId, int maxCount) + { + return StorageManager.GetWaitingMessagesOfApplication(Settings.ThisServerName, Name, minId, maxCount); + } + + /// + /// Gets Id of last incoming message that will be sent to this application. + /// + /// Id of last incoming message + protected override int GetMaxWaitingMessageId() + { + return StorageManager.GetMaxWaitingMessageIdOfApplication(Settings.ThisServerName, Name); + } + + /// + /// Finds Next server for a message. + /// + /// Next server + protected override string GetNextServerForMessage(MDSDataTransferMessage message) + { + //Next server and destination server is same (this server) because message is being delivered to application now. + return message.DestinationServerName; + } + + #endregion + } +} diff --git a/src/MDSCore/Organization/MDSClientApplicationList.cs b/src/MDSCore/Organization/MDSClientApplicationList.cs new file mode 100644 index 0000000..4e782b6 --- /dev/null +++ b/src/MDSCore/Organization/MDSClientApplicationList.cs @@ -0,0 +1,135 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using MDS.Communication.WebServiceCommunication; +using MDS.Exceptions; +using MDS.Settings; +using MDS.Threading; + +namespace MDS.Organization +{ + /// + /// All Client applications that can send/receive messages to/from this MDS server are stored in this class. + /// + public class MDSClientApplicationList : IRunnable + { + #region Public properties + + /// + /// A collection that stores client applications. + /// MDSClientApplication objects count is equals to total application definition in Settings file. + /// + public SortedList Applications { get; private set; } + + /// + /// Reference to settings. + /// + private readonly MDSSettings _settings; + + #endregion + + #region Constrcutors + + /// + /// Contructor. Gets applications from given settings file. + /// + public MDSClientApplicationList() + { + _settings = MDSSettings.Instance; + Applications = new SortedList(); + try + { + CreateApplicationList(); + } + catch (MDSException) + { + throw; + } + catch (Exception ex) + { + throw new MDSException("Can not read settings file.", ex); + } + } + + #endregion + + #region Public methods + + public void Start() + { + foreach (var application in Applications.Values) + { + application.Start(); + } + } + + public void Stop(bool waitToStop) + { + foreach (var application in Applications.Values) + { + application.Stop(waitToStop); + } + } + + public void WaitToStop() + { + foreach (var application in Applications.Values) + { + application.WaitToStop(); + } + } + + #endregion + + #region Private methods + + /// + /// Reads the xml file and creates client applications using _settings. + /// + private void CreateApplicationList() + { + foreach (var application in _settings.Applications) + { + //Create application object + var clientApplication = new MDSClientApplication(application.Name); + + foreach (var channel in application.CommunicationChannels) + { + switch (channel.CommunicationType) + { + case "WebService": + clientApplication.AddCommunicator( + new WebServiceCommunicator( + channel.CommunicationSettings["Url"], + Communication.CommunicationLayer.CreateCommunicatorId() + )); + break; + } + } + + //Add new application to list + Applications.Add(clientApplication.Name, clientApplication); + } + } + + #endregion + } +} diff --git a/src/MDSCore/Organization/MDSController.cs b/src/MDSCore/Organization/MDSController.cs new file mode 100644 index 0000000..57550f7 --- /dev/null +++ b/src/MDSCore/Organization/MDSController.cs @@ -0,0 +1,671 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.IO; +using MDS.Communication.Events; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Exceptions; +using MDS.Serialization; +using MDS.Communication; +using MDS.Communication.Messages; +using MDS.Settings; + +namespace MDS.Organization +{ + /// + /// Represents a MDS controller that can monitor/manage to this server. + /// + public class MDSController : MDSRemoteApplication + { + #region Public fiedls + + /// + /// Communicator type for Controllers. + /// + public override CommunicatorTypes CommunicatorType + { + get { return CommunicatorTypes.Controller; } + } + + /// + /// Reference to Organization Layer. + /// + public OrganizationLayer OrganizationLayer { private get; set; } + + #endregion + + #region Private fields + + /// + /// Reference to Settings. + /// + private readonly MDSSettings _settings; + + /// + /// Reference to Design Settings. + /// + private readonly MDSDesignSettings _designSettings; + + #endregion + + #region Constructors + + /// + /// Creates a new MDSController object. + /// + /// Name of the controller + public MDSController(string name) + : base(name, CommunicationLayer.CreateApplicationId()) + { + _settings = MDSSettings.Instance; + _designSettings = MDSDesignSettings.Instance; + MessageReceived += MDSController_MessageReceived; + } + + #endregion + + #region Public methods + + public override void Start() + { + base.Start(); + + //Register events of remote applications + var applicationList = OrganizationLayer.GetClientApplications(); + foreach (var clientApplication in applicationList) + { + clientApplication.CommunicatorConnected += ClientApplication_CommunicatorConnected; + clientApplication.CommunicatorDisconnected += ClientApplication_CommunicatorDisconnected; + } + } + + #endregion + + #region Private methods + + #region Message handling and processing mehods + + /// + /// Handles MessageReceived event. + /// All messages received from all controllers comes to this method. + /// + /// Sender object + /// Event arguments + private void MDSController_MessageReceived(object sender, MessageReceivedFromRemoteApplicationEventArgs e) + { + try + { + //Response to Ping messages + if ((e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSPingMessage) && string.IsNullOrEmpty(e.Message.RepliedMessageId)) + { + //Reply ping message + SendMessage(new MDSPingMessage { RepliedMessageId = e.Message.MessageId }, e.Communicator); + return; + } + + //Do not process messages other than MDSControllerMessage + if (e.Message.MessageTypeId != MDSMessageFactory.MessageTypeIdMDSControllerMessage) + { + return; + } + + //Cast message to MDSControllerMessage + var controllerMessage = e.Message as MDSControllerMessage; + if (controllerMessage == null) + { + return; + } + + //Create (deserialize) ControlMessage from MessageData of controllerMessage object + var controlMessage = MDSSerializationHelper.DeserializeFromByteArray( + () => + ControlMessageFactory.CreateMessageByTypeId(controllerMessage.ControllerMessageTypeId), + controllerMessage.MessageData); + + //Process message + ProcessControllerMessage(e.Communicator, controllerMessage, controlMessage); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + /// + /// This methods checks type of message (MessageTypeId) and calls appropriate method to process message. + /// + /// Communicator that sent message + /// MDSControllerMessage object that includes controlMessage + /// The message to be processed + private void ProcessControllerMessage(ICommunicator communicator, MDSControllerMessage controllerMessage , ControlMessage controlMessage) + { + switch (controlMessage.MessageTypeId) + { + case ControlMessageFactory.MessageTypeIdGetApplicationListMessage: + ProcessGetApplicationListMessage(communicator, controllerMessage); + break; + case ControlMessageFactory.MessageTypeIdAddNewApplicationMessage: + ProcessAddNewApplicationMessage(controlMessage as AddNewApplicationMessage); + break; + case ControlMessageFactory.MessageTypeIdRemoveApplicationMessage: + ProcessRemoveApplicationMessage(communicator, controlMessage as RemoveApplicationMessage, controllerMessage); + break; + case ControlMessageFactory.MessageTypeIdGetServerGraphMessage: + ProcessGetServerGraphMessage(communicator, controllerMessage); + break; + case ControlMessageFactory.MessageTypeIdUpdateServerGraphMessage: + ProcessUpdateServerGraphMessage(communicator, controlMessage as UpdateServerGraphMessage, controllerMessage); + break; + case ControlMessageFactory.MessageTypeIdGetApplicationWebServicesMessage: + ProcessGetApplicationWebServicesMessage(communicator, controlMessage as GetApplicationWebServicesMessage, controllerMessage); + break; + case ControlMessageFactory.MessageTypeIdUpdateApplicationWebServicesMessage: + ProcessUpdateApplicationWebServicesMessage(communicator, controlMessage as UpdateApplicationWebServicesMessage, controllerMessage); + break; + default: + throw new MDSException("Undefined MessageTypeId for ControlMessage: " + controlMessage.MessageTypeId); + } + } + + #region GetApplicationListMessage + + /// + /// Processes GetApplicationListMessage. + /// + /// Communicator that sent message + /// MDSControllerMessage object that includes controlMessage + private void ProcessGetApplicationListMessage(ICommunicator communicator, MDSControllerMessage controllerMessage) + { + //Get all client applications + var applicationList = OrganizationLayer.GetClientApplications(); + + //Create ClientApplicationInfo array + var clientApplications = new GetApplicationListResponseMessage.ClientApplicationInfo[applicationList.Length]; + for (var i = 0; i < applicationList.Length; i++) + { + clientApplications[i] = new GetApplicationListResponseMessage.ClientApplicationInfo + { + Name = applicationList[i].Name, + CommunicatorCount = applicationList[i].ConnectedCommunicatorCount + }; + } + + //Send response message + ReplyMessageToCommunicator( + communicator, + new GetApplicationListResponseMessage + { + ClientApplications = clientApplications + }, + controllerMessage + ); + } + + #endregion + + #region AddNewApplicationMessage + + /// + /// Processes AddNewApplicationMessage. + /// + /// The message to be processed + private void ProcessAddNewApplicationMessage(AddNewApplicationMessage controlMessage) + { + var addedApplication = OrganizationLayer.AddApplication(controlMessage.ApplicationName); + addedApplication.CommunicatorConnected += ClientApplication_CommunicatorConnected; + addedApplication.CommunicatorDisconnected += ClientApplication_CommunicatorDisconnected; + SendMessageToAllReceivers( + new ClientApplicationRefreshEventMessage + { + Name = addedApplication.Name, + CommunicatorCount = addedApplication.ConnectedCommunicatorCount + }); + } + + #endregion + + #region RemoveApplicationMessage + + /// + /// Processes RemoveApplicationMessage. + /// + /// Communicator that sent message + /// The message to be processed + /// MDSControllerMessage object that includes controlMessage + private void ProcessRemoveApplicationMessage(ICommunicator communicator, RemoveApplicationMessage controlMessage, MDSControllerMessage controllerMessage) + { + try + { + var removedApplication = OrganizationLayer.RemoveApplication(controlMessage.ApplicationName); + removedApplication.CommunicatorConnected -= ClientApplication_CommunicatorConnected; + removedApplication.CommunicatorDisconnected -= ClientApplication_CommunicatorDisconnected; + + ReplyMessageToCommunicator( + communicator, + new RemoveApplicationResponseMessage + { + ApplicationName = controlMessage.ApplicationName, + Removed = true, + ResultMessage = "Success." + }, + controllerMessage + ); + + SendMessageToAllReceivers( + new ClientApplicationRemovedEventMessage + { + ApplicationName = removedApplication.Name + }); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + ReplyMessageToCommunicator( + communicator, + new RemoveApplicationResponseMessage + { + ApplicationName = controlMessage.ApplicationName, + Removed = false, + ResultMessage = ex.Message + }, + controllerMessage + ); + } + } + + #endregion + + #region GetServerGraphMessage + + /// + /// Processes GetServerGraphMessage. + /// + /// Communicator that sent message + /// MDSControllerMessage object that includes controlMessage + private void ProcessGetServerGraphMessage(ICommunicator communicator, MDSControllerMessage controllerMessage) + { + //Create response message + var responseMessage = + new GetServerGraphResponseMessage + { + ServerGraph = + new ServerGraphInfo + { + ThisServerName = _settings.ThisServerName, + Servers = new ServerGraphInfo.ServerOnGraph[_settings.Servers.Count] + } + }; + + //Fill server settings + for (var i = 0; i < _settings.Servers.Count; i++) + { + responseMessage.ServerGraph.Servers[i] = new ServerGraphInfo.ServerOnGraph + { + Name = _settings.Servers[i].Name, + IpAddress = _settings.Servers[i].IpAddress, + Port = _settings.Servers[i].Port, + Adjacents = _settings.Servers[i].Adjacents + }; + } + + //Fill server design settings + for (var i = 0; i < responseMessage.ServerGraph.Servers.Length; i++) + { + foreach (var serverDesignItem in _designSettings.Servers) + { + if (responseMessage.ServerGraph.Servers[i].Name == serverDesignItem.Name) + { + responseMessage.ServerGraph.Servers[i].Location = serverDesignItem.Location; + break; + } + } + } + + //Send response message + ReplyMessageToCommunicator( + communicator, + responseMessage, + controllerMessage + ); + } + + #endregion + + #region UpdateServerGraphMessage + + /// + /// Processes UpdateServerGraphMessage. + /// + /// Communicator that sent message + /// The message to be processed + /// MDSControllerMessage object that includes controlMessage + private void ProcessUpdateServerGraphMessage(ICommunicator communicator, UpdateServerGraphMessage controlMessage, MDSControllerMessage controllerMessage) + { + try + { + var newSettings = new MDSSettings(Path.Combine(GeneralHelper.GetCurrentDirectory(), "MDSSettings.xml")); + var newDesignSettings = new MDSDesignSettings(Path.Combine(GeneralHelper.GetCurrentDirectory(), "MDSSettings.design.xml")); + + //Clear existing server lists + newSettings.Servers.Clear(); + newDesignSettings.Servers.Clear(); + + //Add servers from UpdateServerGraphMessage + newSettings.ThisServerName = controlMessage.ServerGraph.ThisServerName; + foreach (var server in controlMessage.ServerGraph.Servers) + { + //Settings + newSettings.Servers.Add( + new ServerInfoItem + { + Name = server.Name, + IpAddress = server.IpAddress, + Port = server.Port, + Adjacents = server.Adjacents + }); + //Design settings + newDesignSettings.Servers.Add( + new ServerDesignItem + { + Name = server.Name, + Location = server.Location + }); + } + + //Save settings + newSettings.SaveToXml(); + newDesignSettings.SaveToXml(); + } + catch (Exception ex) + { + //Send fail message + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage {Success = false, ResultMessage = ex.Message}, + controllerMessage + ); + return; + } + + //Send success message + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage {Success = true, ResultMessage = "Success"}, + controllerMessage + ); + } + + #endregion + + #region GetApplicationWebServicesMessage + + private void ProcessGetApplicationWebServicesMessage(ICommunicator communicator, GetApplicationWebServicesMessage message, MDSControllerMessage controllerMessage) + { + try + { + //Find application + ApplicationInfoItem application = null; + foreach (var applicationInfoItem in _settings.Applications) + { + if(applicationInfoItem.Name == message.ApplicationName) + { + application = applicationInfoItem; + } + } + + if(application == null) + { + //Send message + ReplyMessageToCommunicator( + communicator, + new GetApplicationWebServicesResponseMessage + { + WebServices = null, + Success = false, + ResultText = "No application found with name '" + message.ApplicationName + "'." + }, + controllerMessage + ); + + return; + } + + var webServiceList = new List(); + foreach (var channel in application.CommunicationChannels) + { + if ("WebService".Equals(channel.CommunicationType, StringComparison.OrdinalIgnoreCase)) + { + webServiceList.Add(new ApplicationWebServiceInfo {Url = channel.CommunicationSettings["Url"]}); + } + } + + //Send web service list + ReplyMessageToCommunicator( + communicator, + new GetApplicationWebServicesResponseMessage + { + WebServices = webServiceList.ToArray(), + Success = true, + ResultText = "Success." + }, + controllerMessage + ); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + #endregion + + #region UpdateApplicationWebServicesMessage + + private void ProcessUpdateApplicationWebServicesMessage(ICommunicator communicator, UpdateApplicationWebServicesMessage message, MDSControllerMessage controllerMessage) + { + try + { + //Find application + ApplicationInfoItem application = null; + foreach (var applicationInfoItem in _settings.Applications) + { + if (applicationInfoItem.Name == message.ApplicationName) + { + application = applicationInfoItem; + } + } + + if (application == null) + { + //Send message + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage() + { + Success = false, + ResultMessage = "No application found with name '" + message.ApplicationName + "'." + }, + controllerMessage + ); + return; + } + + //Delete old service list + application.CommunicationChannels.Clear(); + + //Add new services + if (message.WebServices != null && message.WebServices.Length > 0) + { + foreach (var webServiceInfo in message.WebServices) + { + var channelInfo = new ApplicationInfoItem.CommunicationChannelInfoItem { CommunicationType = "WebService" }; + channelInfo.CommunicationSettings["Url"] = webServiceInfo.Url; + application.CommunicationChannels.Add(channelInfo); + } + } + + try + { + //Save settings + _settings.SaveToXml(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage() + { + Success = false, + ResultMessage = "Can not save XML configuration file (MDSSettings.xml)." + }, + controllerMessage + ); + return; + } + + //Send success message + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage() + { + Success = true, + ResultMessage = "Success." + }, + controllerMessage + ); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + ReplyMessageToCommunicator( + communicator, + new OperationResultMessage() + { + Success = false, + ResultMessage = ex.Message + }, + controllerMessage + ); + return; + } + } + + #endregion + + #endregion + + #region Other Event handling methods + + /// + /// Handles CommunicatorConnected event of all client applications. + /// + /// Creates of event (application) + /// Event arguments + private void ClientApplication_CommunicatorConnected(object sender, CommunicatorConnectedEventArgs e) + { + var application = sender as MDSRemoteApplication; + if (application == null) + { + return; + } + + SendMessageToAllReceivers(new ClientApplicationRefreshEventMessage + { + Name = application.Name, + CommunicatorCount = application.ConnectedCommunicatorCount + }); + } + + /// + /// Handles CommunicatorDisconnected event of all client applications. + /// + /// Creates of event (application) + /// Event arguments + void ClientApplication_CommunicatorDisconnected(object sender, CommunicatorDisconnectedEventArgs e) + { + var application = sender as MDSRemoteApplication; + if (application == null) + { + return; + } + + SendMessageToAllReceivers(new ClientApplicationRefreshEventMessage + { + Name = application.Name, + CommunicatorCount = application.ConnectedCommunicatorCount + }); + } + + #endregion + + #region Other private methods + + /// + /// Sends a ControlMessage to all connected MDSController instances. + /// + /// Message to send + private void SendMessageToAllReceivers(ControlMessage message) + { + var outgoingMessage = new MDSControllerMessage + { + ControllerMessageTypeId = message.MessageTypeId, + MessageData = MDSSerializationHelper.SerializeToByteArray(message) + }; + + var receivers = GetAllReceiverCommunicators(); + foreach (var receiver in receivers) + { + try + { + SendMessage(outgoingMessage, receiver); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + } + + /// + /// Sends a message to a spesific communicator as a reply to an incoming message. + /// + /// Communicator to send message + /// Message to send + /// Incoming message which is being replied + private void ReplyMessageToCommunicator(ICommunicator communicator, ControlMessage message, MDSControllerMessage incomingMessage) + { + //Create MDSControllerMessage that includes serialized GetApplicationListResponseMessage message + var outgoingMessage = new MDSControllerMessage + { + ControllerMessageTypeId = message.MessageTypeId, + MessageData = MDSSerializationHelper.SerializeToByteArray(message), + RepliedMessageId = incomingMessage.MessageId + }; + //Send message to communicator that sent to message + SendMessage(outgoingMessage, communicator); + } + + #endregion + + #endregion + } +} diff --git a/src/MDSCore/Organization/MDSPersistentRemoteApplicationBase.cs b/src/MDSCore/Organization/MDSPersistentRemoteApplicationBase.cs new file mode 100644 index 0000000..2ef0d2f --- /dev/null +++ b/src/MDSCore/Organization/MDSPersistentRemoteApplicationBase.cs @@ -0,0 +1,652 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Threading; +using MDS.Communication; +using MDS.Communication.Events; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Storage; + +namespace MDS.Organization +{ + /// + /// This class extends MDSRemoteApplication, adds functionality to send/receive persistent messages. + /// + public abstract class MDSPersistentRemoteApplicationBase : MDSRemoteApplication + { + #region Consts + + /// + /// Maximum message count can be stored in _waitingMessages. + /// + private const int MaxMessagesInQueue = 50; + + /// + /// If message count in _waitingMessages decrees under this count and there are new messages on database, new messages are received to queue. + /// This value must be smaller (or equal but not recommended) than MaxMessagesInQueue. + /// + private const int MinMessageCountToGetFromDatabase = 5; + + /// + /// In an database, network... error, thread first waits FirstWaitingTimeOnError milliseconds before next try. + /// + private const int FirstWaitingTimeOnError = 1000; + + /// + /// In a database, network... error, thread maximum waits MaxWaitingTimeOnError milliseconds before next try. + /// + private const int MaxWaitingTimeOnError = 60000; + + #endregion + + #region Public fields + + /// + /// Reference to the Store manager in MDS server. + /// + public IStorageManager StorageManager { get; set; } + + #endregion + + #region Private fields + + /// + /// Waiting message list that contains cached messages to be sent to this remote application. + /// New messages are inserted to storage manager (database) and also added to this queue. Thus, when message is sent to remote application, + /// it is gooten from this queue instead of database. This improves performance. + /// When this list has item less than MinMessageCountToGetFromDatabase and database has messages, the list is filled from database. + /// + private readonly LinkedList _waitingMessages; + + /// + /// Biggest Id in database that is waiting to be sent to this remote application. + /// If no records exists, this is last sent message's Id. + /// + private int _biggestWaitingMessageId; + + /// + /// Biggest Id in _waitingMessages (Last item's Id). + /// + private int _biggestWaitingMessageIdInList; + + /// + /// Runs on ProcessWaitingMessageRecordsAsThread method. + /// Used to process messages in _waitingMessages. + /// + private Thread _waitingMessageProcessThread; + + /// + /// Indicates that _waitingMessageProcessThread is running or not. + /// + private volatile bool _waitingMessageProcessRunning; + + /// + /// Waiting time on an error situation. + /// It is used with FirstWaitingTimeOnError and MaxWaitingTimeOnError to wait before next try when an error occured. + /// + private int _waitingTimeOnError = FirstWaitingTimeOnError; + + /// + /// This is true if _waitingMessageProcessThread is waiting because of an error situation. + /// In this case, it can not be pulsed and waits to finish it's timeout (except service is stopping). + /// + private volatile bool _waitingForAnError; + + #endregion + + #region Constructors + + /// + /// Constructor. + /// + /// Name of the remote application + protected MDSPersistentRemoteApplicationBase(string name) : + base(name, CommunicationLayer.CreateApplicationId()) + { + _waitingMessages = new LinkedList(); + _biggestWaitingMessageId = 0; + _biggestWaitingMessageIdInList = 0; + } + + #endregion + + #region Public methods + + /// + /// Starts this remote application. + /// + public override void Start() + { + //Lock collection to be synchronized. + lock (_waitingMessages) + { + //Get biggest Id value from database. + _biggestWaitingMessageId = GetMaxWaitingMessageId(); + + //Start _waitingMessageProcessThread + _waitingMessageProcessRunning = true; + _waitingMessageProcessThread = new Thread(ProcessWaitingMessageRecordsAsThread); + _waitingMessageProcessThread.Start(); + + //Start base class + base.Start(); + } + } + + /// + /// Stops this remote application. + /// + /// This is set to true if called thread wants to wait until this application completely stops + public override void Stop(bool waitToStop) + { + lock (_waitingMessages) + { + //Stop base class + base.Stop(waitToStop); + + //Stop _waitingMessageProcessThread + _waitingMessageProcessRunning = false; + Monitor.PulseAll(_waitingMessages); + } + + if(waitToStop) + { + WaitToStop(); + } + } + + /// + /// Waits until this application completely stops. + /// No action if it is already stopped. + /// + public override void WaitToStop() + { + //Wait to stop of base class + base.WaitToStop(); + + //If _waitingMessageProcessThread is null, that means it is already stopped. + if (_waitingMessageProcessThread == null) + { + return; + } + + try + { + //Wait to stop of _waitingMessageProcessThread + _waitingMessageProcessThread.Join(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// This method is called when a MDSDataTransferMessage sent to this application. + /// Stores message and adds to queue to send to remote application. + /// + /// Message + public void EnqueueMessage(MDSDataTransferMessage message) + { + //Create MDSMessageRecord from MDSDataTransferMessage to save message to database + var messageRecord = new MDSMessageRecord(message) {NextServer = GetNextServerForMessage(message)}; + + //Lock collection to be synchronized. + lock (_waitingMessages) + { + if (message.TransmitRule == MessageTransmitRules.StoreAndForward) + { + //Save message to database + StorageManager.StoreMessage(messageRecord); + /* If these conditions are true, then message also added to _waitingMessages + * and message is sent to appliction from this queue instead of read again from database (for performance reasons): + * - _waitingMessages's message count is smaller than a maximum count (_waitingMessages.Count < MaxMessagesInQueue). + * - All messages in database is also in _waitingMessages (_biggestWaitingMessageIdInList >= _biggestWaitingMessageId) + * That means there is no message that is in database but not in _waitingMessages list. + */ + if (_waitingMessages.Count < MaxMessagesInQueue && + _biggestWaitingMessageIdInList >= _biggestWaitingMessageId) + { + //Add message to queue. + _waitingMessages.AddLast(new WaitingMessage(messageRecord)); + //This message's Id is new biggest id on queue. + _biggestWaitingMessageIdInList = messageRecord.Id; + } + + //This message's id is new biggest id in database, so update _biggestWaitingMessageId value + _biggestWaitingMessageId = messageRecord.Id; + } + else + { + //Add message to queue. + _waitingMessages.AddFirst(new WaitingMessage(messageRecord)); + } + + //Pulse waiting thread that is in wait state because of no message to process. + if (!_waitingForAnError) + { + Monitor.PulseAll(_waitingMessages); + } + + Logger.Debug("EnqueueMessage - WaitingMessages.Count = " + _waitingMessages.Count + ", Application = " + Name); + } + } + + /// + /// This method is called when a MDSDataTransferMessage sent to this application. + /// It does not store message, adds it as first item of sending queue. + /// + /// Message + public void AddMessageToHeadOfQueue(MDSDataTransferMessage message) + { + //Lock collection to be synchronized. + lock (_waitingMessages) + { + //Add message to queue. + _waitingMessages.AddFirst( + new WaitingMessage( + new MDSMessageRecord(message) + { + NextServer = GetNextServerForMessage(message), + Id = -1 + })); + + //Pulse waiting thread that is in wait state because of no message to process. + if (!_waitingForAnError) + { + Monitor.PulseAll(_waitingMessages); + } + + Logger.Debug("AddMessageToHeadOfQueue - WaitingMessages.Count = " + _waitingMessages.Count + ", Application = " + Name); + } + } + + #endregion + + #region Protected methods + + /// + /// This method handles ACK/Reject messages from remote application for a data transfer message. + /// + /// Communicator that sent message + /// Response message + protected override void OnResponseReceived(ICommunicator communicator, MDSOperationResultMessage operationResultMessage) + { + base.OnResponseReceived(communicator, operationResultMessage); + EvaluateResponse(communicator, operationResultMessage, null); + } + + #endregion + + #region Private methods + + /// + /// This method is run by _waitingMessageProcessThread and ensures persistence with EnqueueMessage method. + /// + private void ProcessWaitingMessageRecordsAsThread() + { + Logger.Debug("MDSPersistentRemoteApplicationBase - ProcessWaitingMessage thread is started. ApplicationId = " + ApplicationId); + + //Loop until this remote application stops (by Stop method) + while (_waitingMessageProcessRunning) + { + try + { + ProcessWaitingMessageRecords(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + Logger.Debug("MDSPersistentRemoteApplicationBase - ProcessWaitingMessage thread is stopped. ApplicationId = " + ApplicationId); + + _waitingMessageProcessThread = null; + } + + /// + /// This is the main method that is called in ProcessWaitingMessageRecordsAsThread method. + /// In basic, it gets a record from queue/database and sends it to the remote application. + /// + private void ProcessWaitingMessageRecords() + { + try + { + WaitingMessage waitingMessage; + + //Lock collection to be synchronized. + lock (_waitingMessages) + { + GetMessagesFromDatabaseIfNeeded(); + waitingMessage = GetFirstReadyMessageToSend(); + if (waitingMessage == null) + { + //Wait for a new incoming message pulse (by StoreAndEnqueue or AddMessageToHeadOfQueue methods) or undelivered message pulse (by OnResponseReceived method) + Monitor.Wait(_waitingMessages); + } + + Logger.Debug("ProcessWaitingMessageRecords - WaitingMessages.Count = " + _waitingMessages.Count + ", Application = " + Name); + } + + //If a message is gotten from queue then process it + if (waitingMessage != null) + { + ProcessWaitingMessage(waitingMessage); + } + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + WaitOnError(); + return; + } + } + + /// + /// Gets a message from _waitingMessages that is ready to send (not processing by a communicator and waiting for ACK). + /// + /// A message that is ready to send. Returns null if there is no message to send + private WaitingMessage GetFirstReadyMessageToSend() + { + for (var waitingMessage = _waitingMessages.First; waitingMessage != null; waitingMessage = waitingMessage.Next ) + { + if (waitingMessage.Value.State == WaitingMessage.WaitingMessageStates.ReadyToSend) + { + return waitingMessage.Value; + } + } + + return null; + } + + /// + /// Gets messages from database if needed. + /// + private void GetMessagesFromDatabaseIfNeeded() + { + /* Messages are gotten from database if both of these conditions are true: + * - Messages exists in database that are not in (_waitingMessages) queue (_biggestWaitingMessageId > _biggestWaitingMessageIdInList) + * - Message count in (_waitingMessages) list is smaller than a minimum amount (_waitingMessages.Count < MinMessageCountToGetFromDatabase) + */ + if (_biggestWaitingMessageId > _biggestWaitingMessageIdInList && _waitingMessages.Count < MinMessageCountToGetFromDatabase) + { + //Get messages from database + var newRecords = GetWaitingMessages(_biggestWaitingMessageIdInList + 1, MaxMessagesInQueue - _waitingMessages.Count); + foreach (var newRecord in newRecords) + { + //Add message to queue + _waitingMessages.AddLast(new WaitingMessage(newRecord)); + //Update biggest Id on queue + _biggestWaitingMessageIdInList = newRecord.Id; + } + } + } + + /// + /// Process one MDSMessageRecord from queue. + /// Called by ProcessWaitingMessageRecords method for each message. + /// If waitingMessage.MessageRecord.Id > 0 that means the message is asyncronous (added to queue with EnqueueMessage and stored to database), + /// else the message is syncronous (added to queue with AddMessageToHeadOfQueue method). + /// Asyncronous messages will be tried again and again until it is delivered, + /// but syncronsous messages is tried only one time to send, because sender application waits response for it. + /// + /// Message to process + private void ProcessWaitingMessage(WaitingMessage waitingMessage) + { + try + { + waitingMessage.State = WaitingMessage.WaitingMessageStates.WaitingForAcknowledgment; + //If message is stored to database (waitingMessage.MessageRecord.Id > 0)... + if (waitingMessage.MessageRecord.Id >= 0) + { + //Set DestinationCommunicatorId = 0, because this field can not be used on persistent messages. + waitingMessage.MessageRecord.Message.DestinationCommunicatorId = 0; + //No timeout value, so, wait a free communicator for infinity + SendDataMessage(waitingMessage.MessageRecord.Message, 0); + } + else + { + SendDataMessage(waitingMessage.MessageRecord.Message, Settings.MessageResponseTimeout); + } + } + catch (MDSNoCommunicatorException ex) + { + Logger.Warn(ex.Message, ex); + EvaluateResponse(null, new MDSOperationResultMessage + { + RepliedMessageId = waitingMessage.MessageRecord.MessageId, + ResultText = ex.Message, + Success = false + }, waitingMessage); + //If it was stored message, stop processing messages until a receiver communicator is connected + if (waitingMessage.MessageRecord.Id >= 0) + { + WaitUntilReceiverCommunicatorConnected(); + } + } + catch (MDSTimeoutException ex) + { + Logger.Warn(ex.Message, ex); + EvaluateResponse(null, new MDSOperationResultMessage + { + RepliedMessageId = waitingMessage.MessageRecord.MessageId, + ResultText = ex.Message, + Success = false + }, waitingMessage); + //If it was stored message, wait a while before processing messages + if (waitingMessage.MessageRecord.Id >= 0) + { + WaitOnError(); + } + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + EvaluateResponse(null, new MDSOperationResultMessage + { + RepliedMessageId = waitingMessage.MessageRecord.MessageId, + ResultText = ex.Message, + Success = false + }, waitingMessage); + //If it was stored message, wait a while before processing messages + if (waitingMessage.MessageRecord.Id >= 0) + { + WaitOnError(); + } + } + } + + /// + /// This method evaluates ACK/Reject messages from remote application for a data transfer message. + /// + /// Communicator that sent message + /// Response message + /// Waiting message for this response (may be null, if it is null, it is searched in _waitingMessages list) + private void EvaluateResponse(ICommunicator communicator, MDSOperationResultMessage operationResultMessage, WaitingMessage waitingMessage) + { + lock (_waitingMessages) + { + //If waitingMessage argument is not supplied, find waiting message from _waitingMessages list + if (waitingMessage == null) + { + foreach (var wMessage in _waitingMessages) + { + if (wMessage.State == WaitingMessage.WaitingMessageStates.WaitingForAcknowledgment && + wMessage.MessageRecord.MessageId == operationResultMessage.RepliedMessageId) + { + waitingMessage = wMessage; + break; + } + } + + //If there is not message that waits for this response, just exit from method.. + if (waitingMessage == null) + { + return; + } + } + + //If message is stored to database in this server + if (waitingMessage.MessageRecord.Id >= 0) + { + if (operationResultMessage.Success) + { + //If message is persistent (Stored on database), remove from database. + if (waitingMessage.MessageRecord.Id > 0) + { + StorageManager.RemoveMessage(waitingMessage.MessageRecord.Id); + } + + //Remove from list + _waitingMessages.Remove(waitingMessage); + //Reset _waitingTimeOnError (because this is a success situation) + _waitingTimeOnError = FirstWaitingTimeOnError; + } + else + { + //Set message state as 'ready to send' again. Thus, message is added to send queue again. + waitingMessage.State = WaitingMessage.WaitingMessageStates.ReadyToSend; + //Pulse if a thread waiting in ProcessWaitingMessageRecords method + Monitor.PulseAll(_waitingMessages); + } + } + //If message is not stored to database in this server + else + { + //Remove from list + _waitingMessages.Remove(waitingMessage); + } + + Logger.Debug("EvaluateResponse - WaitingMessages.Count = " + _waitingMessages.Count + ", Application = " + Name); + } + + //If it is not stored message.. + if (waitingMessage.MessageRecord.Id < 0) + { + //Create event to send response message to upper layers + OnMessageReceived(this, new MessageReceivedFromCommunicatorEventArgs + { + Communicator = communicator, + Message = operationResultMessage + }); + } + } + + /// + /// Stops _waitingMessageProcessThread thread for a while on an error situation. + /// + private void WaitOnError() + { + _waitingForAnError = true; + try + { + lock (_waitingMessages) + { + //Calculating waiting time + var waitingTime = Math.Min(_waitingTimeOnError, MaxWaitingTimeOnError); + //Calculate waiting time on next error + _waitingTimeOnError = Math.Min(waitingTime * 2, MaxWaitingTimeOnError); + //Wait.. + Monitor.Wait(_waitingMessages, waitingTime); + } + } + finally + { + _waitingForAnError = false; + } + } + + #endregion + + #region Abstract methods + + /// + /// Gets messages from database to be sent to this remote application. + /// + /// Minimum Id of message record to get (minId included) + /// Maximum number of records to get + /// List of messages + protected abstract List GetWaitingMessages(int minId, int maxCount); + + /// + /// Gets Id of last incoming message that will be sent to this remote application. + /// + /// Id of last incoming message + protected abstract int GetMaxWaitingMessageId(); + + /// + /// Finds Next server for a message. + /// This method is designed, because it is different to get next server's name for client applications and mds servers. + /// + /// Next server + protected abstract string GetNextServerForMessage(MDSDataTransferMessage message); + + #endregion + + #region Sub classes + + /// + /// This class represents a message in _waitingMessages list. + /// + private class WaitingMessage + { + /// + /// Message record in storage manager. + /// + public MDSMessageRecord MessageRecord { get; private set; } + + /// + /// State of the message. + /// + public WaitingMessageStates State { get; set; } + + /// + /// States of a messages. + /// + public enum WaitingMessageStates + { + /// + /// This message is waiting to be sent to remote application. + /// + ReadyToSend, + + /// + /// This message is sent to remote application and waiting an ACK message to be removed from _waitingMessages list. + /// + WaitingForAcknowledgment + } + + /// + /// Creates a new WaitingMessage object. + /// + /// Message record in storage manager + public WaitingMessage(MDSMessageRecord messageRecord) + { + MessageRecord = messageRecord; + State = WaitingMessageStates.ReadyToSend; + } + } + + #endregion + } +} diff --git a/src/MDSCore/Organization/MDSServerGraph.cs b/src/MDSCore/Organization/MDSServerGraph.cs new file mode 100644 index 0000000..9735177 --- /dev/null +++ b/src/MDSCore/Organization/MDSServerGraph.cs @@ -0,0 +1,423 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Exceptions; +using MDS.Settings; +using MDS.Threading; + +namespace MDS.Organization +{ + /// + /// Represents all servers on network as a graph. + /// And also stores references to all communicating adjacent servers. + /// + public class MDSServerGraph : IRunnable + { + #region Public properties + + /// + /// Gets this (current) server node. + /// + public MDSServerNode ThisServerNode { get; private set; } + + /// + /// All server nodes on network. + /// + public SortedList ServerNodes { get; private set; } + + /// + /// A collection that stores communicating adjacent MDS servers to this MDS server. + /// + public SortedList AdjacentServers { get; private set; } + + #endregion + + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to settings. + /// + private readonly MDSSettings _settings; + + /// + /// This Timer is used to check mds server connections, + /// send ping messages and reconnect if needed. + /// + private readonly Timer _pingTimer; + + /// + /// This flag is used to start/stop _pingTimer. + /// + private volatile bool _running; + + #endregion + + #region Constructors + + /// + /// Contructor. + /// + public MDSServerGraph() + { + _settings = MDSSettings.Instance; + ServerNodes = new SortedList(); + AdjacentServers = new SortedList(); + _pingTimer = new Timer(PingTimer_Elapsed, null, Timeout.Infinite, Timeout.Infinite); + try + { + CreateGraph(); + } + catch (MDSException) + { + throw; + } + catch (Exception ex) + { + throw new MDSException("Can not read settings file. Detail: " + ex.Message, ex); + } + } + + #endregion + + #region Public methods + + public void Start() + { + foreach (var server in AdjacentServers.Values) + { + server.Start(); + } + + _running = true; + _pingTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + + public void Stop(bool waitToStop) + { + lock (_pingTimer) + { + _running = false; + _pingTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + foreach (var server in AdjacentServers.Values) + { + server.Stop(waitToStop); + } + } + + public void WaitToStop() + { + foreach (var server in AdjacentServers.Values) + { + server.WaitToStop(); + } + } + + /// + /// Calculates all next (adjacent) servers for all destination servers. + /// + /// + /// List of "Destination server name - Next server name" pairs. + /// For example: + /// If path is "ThisServer - ServerB - ServerC" than Destination server is ServerC and NextServer is ServerB. + /// + public List> GetNextServersForDestServers() + { + var list = new List>(ServerNodes.Count); + list.Add(new KeyValuePair(ThisServerNode.Name, ThisServerNode.Name)); + + foreach (var destNode in ServerNodes.Values) + { + if (!ThisServerNode.BestPathsToServers.ContainsKey(destNode.Name)) + { + continue; + } + + var bestPath = ThisServerNode.BestPathsToServers[destNode.Name]; + if (bestPath.Count > 1) + { + list.Add(new KeyValuePair(destNode.Name, bestPath[1].Name)); + } + } + + return list; + } + + #endregion + + #region Private methods + + #region Initializing methods + + /// + /// Creates graph according to settings. + /// + private void CreateGraph() + { + CreateServerNodes(); + JoinNodes(); + SetCurrentServer(); + CreateAdjacentServers(); + CalculateShortestPaths(); + } + + /// + /// Creates ServerNodes list from _settings. + /// + private void CreateServerNodes() + { + foreach (var server in _settings.Servers) + { + ServerNodes.Add(server.Name, new MDSServerNode(server.Name)); + } + } + + /// + /// Gets adjacent nodes from settings and joins servers in ServerNodes. + /// + private void JoinNodes() + { + //Gets adjacents of all nodes + var adjacentsOfServers = new SortedList(); + foreach (var server in _settings.Servers) + { + adjacentsOfServers.Add(server.Name, server.Adjacents); + } + + //Join adjacents of all nodes + foreach (var serverName in adjacentsOfServers.Keys) + { + //Create adjacent list + ServerNodes[serverName].Adjacents = new SortedList(); + //Get adjacent names + var adjacents = adjacentsOfServers[serverName].Split(','); + //Add nodes as adjacent + foreach (var adjacent in adjacents) + { + var trimmedAdjacentName = adjacent.Trim(); + if (string.IsNullOrEmpty(trimmedAdjacentName)) + { + continue; + } + + if (!ServerNodes.ContainsKey(trimmedAdjacentName)) + { + throw new MDSException("Adjacent server (" + trimmedAdjacentName + ") of server (" + serverName + ") can not be found in servers list."); + } + + ServerNodes[serverName].Adjacents.Add(trimmedAdjacentName, ServerNodes[trimmedAdjacentName]); + } + } + } + + /// + /// Sets ThisServerNode field according to _settings and ServerNodes + /// + private void SetCurrentServer() + { + if (ServerNodes.ContainsKey(_settings.ThisServerName)) + { + ThisServerNode = ServerNodes[_settings.ThisServerName]; + } + else + { + throw new MDSException("Current server is not defined in settings file."); + } + } + + /// + /// Fills AdjacentServers collection according to settings. + /// + private void CreateAdjacentServers() + { + //Create MDSAdjacentServer objects to communicate with adjacent servers of this Server + foreach (var server in _settings.Servers) + { + //If the node is this server, get IP and Port informations + if (server.Name == _settings.ThisServerName) + { + _settings["__ThisServerTCPPort"] = server.Port.ToString(); + } + //If the node is adjacent to this server, create a MDSAdjacentServer object to communicate + else if (ThisServerNode.Adjacents.ContainsKey(server.Name)) + { + AdjacentServers.Add(server.Name, new MDSAdjacentServer(server.Name, server.IpAddress, server.Port)); + } + } + } + + /// + /// Calculates all shorted paths from all nodes to other nodes. + /// + private void CalculateShortestPaths() + { + //Find shortest paths from all servers to all servers + foreach (var sourceNodeName in ServerNodes.Keys) + { + ServerNodes[sourceNodeName].BestPathsToServers = new SortedList>(); + foreach (var destinationNodeName in ServerNodes.Keys) + { + //Do not search if source and destination nodes are same + if (sourceNodeName == destinationNodeName) + { + continue; + } + + var shortestPath = FindShortestPath(ServerNodes[sourceNodeName], ServerNodes[destinationNodeName]); + if (shortestPath == null) + { + throw new MDSException("There is no path from server '" + sourceNodeName + "' to server '" + destinationNodeName + "'"); + } + + ServerNodes[sourceNodeName].BestPathsToServers[destinationNodeName] = shortestPath; + } + } + } + + /// + /// Find one of the shortest paths from given source node to destination node. + /// + /// Source node + /// Destination node + /// A path from source to destination + private static List FindShortestPath(MDSServerNode sourceServerNode, MDSServerNode destServerNode) + { + //Find all paths + var allPaths = new List>(); + FindPaths(sourceServerNode, destServerNode, allPaths, new List()); + + //Get shortest + if (allPaths.Count > 0) + { + var bestPath = allPaths[0]; + for (var i = 1; i < allPaths.Count; i++) + { + if (bestPath.Count > allPaths[i].Count) + { + bestPath = allPaths[i]; + } + } + + return bestPath; + } + + //No path from sourceServerNode to destServerNode + return null; + } + + /// + /// Finds all the paths from currentServerNode to destServerNode as a recursive method. + /// Passes all nodes, if destination node found, it is added to paths + /// + /// Current server node + /// Destination server node + /// All possible paths are inserted to this list + /// + private static void FindPaths(MDSServerNode currentServerNode, MDSServerNode destServerNode, ICollection> paths, ICollection passedNodes) + { + //Add current node to passedNodes to prevent multi-pass over same node + passedNodes.Add(currentServerNode); + + //If current node is destination, then add passed nodes to found paths. + if (currentServerNode == destServerNode) + { + var foundPath = new List(); + foundPath.AddRange(passedNodes); + paths.Add(foundPath); + } + //Else, Jump to adjacents nodes of current node and conitnue searching + else + { + foreach (var adjacentServerNode in currentServerNode.Adjacents.Values) + { + //If passed over this adjacentServerNode before, skip it + if (passedNodes.Contains(adjacentServerNode)) + { + continue; + } + + //Search path from this adjacent server to destination (recursive call) + FindPaths(adjacentServerNode, destServerNode, paths, passedNodes); + //Remove node from passed nodes, because we may pass over this node for searching another path + passedNodes.Remove(adjacentServerNode); + } + } + } + + #endregion + + #region Checking server connections periodically + + /// + /// This method is called by _pingTimer periodically. + /// + /// Not used argument + private void PingTimer_Elapsed(object state) + { + try + { + //Stop ping timer temporary + _pingTimer.Change(Timeout.Infinite, Timeout.Infinite); + + //Check connection for all adjacent servers + foreach (var server in AdjacentServers.Values) + { + try + { + server.CheckConnection(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + finally + { + //Schedule ping timer for next running if MDSServerGraph is still running. + lock (_pingTimer) + { + if (_running) + { + _pingTimer.Change(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); + } + } + } + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/src/MDSCore/Organization/MDSServerNode.cs b/src/MDSCore/Organization/MDSServerNode.cs new file mode 100644 index 0000000..1c1c603 --- /dev/null +++ b/src/MDSCore/Organization/MDSServerNode.cs @@ -0,0 +1,53 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; + +namespace MDS.Organization +{ + /// + /// Represents a MDS server on network. + /// + public class MDSServerNode + { + /// + /// Name of the remote application + /// + public string Name { get; set; } + + /// + /// Adjacent server nodes of this node. + /// + public SortedList Adjacents { get; set; } + + /// + /// Stores best paths to the all server nodes from this node + /// + public SortedList> BestPathsToServers { get; set; } + + /// + /// Constructur. + /// + /// name of server + public MDSServerNode(string name) + { + Name = name; + } + } +} diff --git a/src/MDSCore/Organization/OrganizationLayer.cs b/src/MDSCore/Organization/OrganizationLayer.cs new file mode 100644 index 0000000..e7045eb --- /dev/null +++ b/src/MDSCore/Organization/OrganizationLayer.cs @@ -0,0 +1,768 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Communication.Events; +using MDS.Exceptions; +using MDS.Organization.Routing; +using MDS.Settings; +using MDS.Storage; +using MDS.Threading; +using MDS.Communication; +using MDS.Communication.Messages; + +namespace MDS.Organization +{ + /// + /// This class represents organization layer of MDS Server. It handles, stores, routes and delivers messages. + /// + public class OrganizationLayer : IRunnable + { + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to settings. + /// + private readonly MDSSettings _settings; + + /// + /// Reference to communication layer. + /// + private readonly CommunicationLayer _communicationLayer; + + /// + /// Reference to storage manager. + /// + private readonly IStorageManager _storageManager; + + /// + /// Routing table. + /// + private readonly RoutingTable _routingTable; + + /// + /// Reference to server graph. + /// + private readonly MDSServerGraph _serverGraph; + + /// + /// Reference to application list. + /// + private readonly MDSClientApplicationList _clientApplicationList; + + /// + /// Reference to all MDS Manager. It contains communicators to all instances of MDS manager. + /// So, there is only one MDSController object in MDS. + /// + private readonly MDSController _mdsManager; + + /// + /// This collection is used to send message and get response in SendMessageDirectly method. + /// SendMessageDirectly method must wait until response received. It waits using this collection. + /// Key: Message ID to wait response. + /// Value: ManualResetEvent to wait thread until response received. + /// + private readonly SortedList _waitingMessages; + + #endregion + + #region Constructors + + /// + /// Constructor. + /// + /// Reference to the Communication Layer + /// Reference to the Storage Manager + /// Reference to the routing table + /// Reference to server graph + /// Reference to application list + /// Reference to MDS Manager object + public OrganizationLayer(CommunicationLayer communicationLayer, IStorageManager storageManager, RoutingTable routingTable, MDSServerGraph serverGraph, MDSClientApplicationList clientApplicationList, MDSController mdsManager) + { + _settings = MDSSettings.Instance; + _communicationLayer = communicationLayer; + _storageManager = storageManager; + _routingTable = routingTable; + _serverGraph = serverGraph; + _clientApplicationList = clientApplicationList; + _mdsManager = mdsManager; + _waitingMessages = new SortedList(); + PrepareCommunicationLayer(); + } + + #endregion + + #region Public methods + + #region Starting / Stopping to Organization layer + + /// + /// Starts the organization layer. + /// + public void Start() + { + _clientApplicationList.Start(); + _serverGraph.Start(); + _mdsManager.Start(); + } + + /// + /// Stops the organization layer. + /// + /// True, if caller thread must be blocked until organization layer stops. + public void Stop(bool waitToStop) + { + _mdsManager.Stop(waitToStop); + _serverGraph.Stop(waitToStop); + _clientApplicationList.Stop(waitToStop); + } + + /// + /// Waits to stop of organization layer. + /// + public void WaitToStop() + { + _mdsManager.WaitToStop(); + _serverGraph.WaitToStop(); + _clientApplicationList.WaitToStop(); + } + + #endregion + + #region Other public methods + + #region Client Application related methods + + /// + /// Gets a list of all client applications as an array. + /// + /// Client applications array + public MDSClientApplication[] GetClientApplications() + { + lock (_clientApplicationList.Applications) + { + return _clientApplicationList.Applications.Values.ToArray(); + } + } + + /// + /// This method is used to add a new client application to MDS while MDS is running. + /// Used by MDSController to allow user to add a new application from MDSManager GUI. + /// It does all necessary tasks to add new application (Updates XML file, adds application to needed + /// collections of system...). + /// + /// Name of the new application + public MDSClientApplication AddApplication(string name) + { + //Add to settings + lock (_settings.Applications) + { + _settings.Applications.Add( + new ApplicationInfoItem + { + Name = name + }); + _settings.SaveToXml(); + } + + //Add to applications list + lock (_clientApplicationList.Applications) + { + var newApplication = new MDSClientApplication(name); + newApplication.Settings = _settings; + newApplication.StorageManager = _storageManager; + newApplication.MessageReceived += RemoteApplication_MessageReceived; + + _clientApplicationList.Applications.Add(name, newApplication); + _communicationLayer.AddRemoteApplication(newApplication); + + newApplication.Start(); + + return newApplication; + } + } + + /// + /// This method is used to delete a client application from MDS while MDS is running. + /// Used by MDSController to allow user to remove an application from MDSManager GUI. + /// It does all necessary tasks to remove application (Updates XML file, removes application from needed + /// collections of system...). + /// + /// Name of the application to remove + /// Removed client application + public MDSClientApplication RemoveApplication(string name) + { + //Remove from application list and communicaton layer + MDSClientApplication clientApplication = null; + lock (_clientApplicationList.Applications) + { + if (_clientApplicationList.Applications.ContainsKey(name)) + { + clientApplication = _clientApplicationList.Applications[name]; + if (clientApplication.ConnectedCommunicatorCount > 0) + { + throw new MDSException("Client application can not be removed. It has " + + clientApplication.ConnectedCommunicatorCount + + " communicators connected. Please stop theese communicators first."); + } + + clientApplication.MessageReceived -= RemoteApplication_MessageReceived; + + _communicationLayer.RemoveRemoteApplication(clientApplication); + _clientApplicationList.Applications.Remove(name); + } + } + + if (clientApplication == null) + { + throw new MDSException("There is no client application with name " + name); + } + + clientApplication.Stop(true); + + //Remove from settings + lock (_settings.Applications) + { + for (var i = 0; i < _settings.Applications.Count;i++ ) + { + if (_settings.Applications[i].Name.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + _settings.Applications.RemoveAt(i); + _settings.SaveToXml(); + break; + } + } + } + + return clientApplication; + } + + #endregion + + #endregion + + #endregion + + #region Private methods + + #region Initializing part + + /// + /// Adds server and applications to communication layer and registers their MessageReceived events. + /// + private void PrepareCommunicationLayer() + { + //Add MDS servers to communication layer, set needed objects and register events + foreach (var adjacentServer in _serverGraph.AdjacentServers.Values) + { + adjacentServer.Settings = _settings; + _communicationLayer.AddRemoteApplication(adjacentServer); + adjacentServer.StorageManager = _storageManager; + adjacentServer.MessageReceived += RemoteApplication_MessageReceived; + } + + //Add applications to communication layer, set needed objects and register events + foreach (var clientApplication in _clientApplicationList.Applications.Values) + { + clientApplication.Settings = _settings; + _communicationLayer.AddRemoteApplication(clientApplication); + clientApplication.StorageManager = _storageManager; + clientApplication.MessageReceived += RemoteApplication_MessageReceived; + } + + _mdsManager.Settings = _settings; + _communicationLayer.AddRemoteApplication(_mdsManager); + } + + #endregion + + #region Incoming message handling + + #region Incoming message handling from MDS Serves / Client Applications + + /// + /// This method is handles all adjacent server's and client application's MessageReceived events. + /// + /// Creator of event + /// Event arguments + private void RemoteApplication_MessageReceived(object sender, MessageReceivedFromRemoteApplicationEventArgs e) + { + try + { + //Incoming data transfer message request + if (e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSDataTransferMessage) + { + //Get, check and process message + var dataTransferMessage = e.Message as MDSDataTransferMessage; + ProcessDataTransferMessage(e.Application as MDSPersistentRemoteApplicationBase, e.Communicator, dataTransferMessage); + } + //Incoming delivery result (ACK/Reject message) + else if ((e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSOperationResultMessage) && + (!string.IsNullOrEmpty(e.Message.RepliedMessageId))) + { + //Find and send signal/pulse to waiting thread for this message + WaitingMessage waitingMessage = null; + lock (_waitingMessages) + { + if (_waitingMessages.ContainsKey(e.Message.RepliedMessageId)) + { + waitingMessage = _waitingMessages[e.Message.RepliedMessageId]; + } + } + + if(waitingMessage != null) + { + waitingMessage.ResponseMessage = e.Message as MDSOperationResultMessage; + waitingMessage.WaitEvent.Set(); + } + } + //Incoming Ping message + else if ((e.Message.MessageTypeId == MDSMessageFactory.MessageTypeIdMDSPingMessage) && + string.IsNullOrEmpty(e.Message.RepliedMessageId)) + { + //Reply ping message + e.Application.SendMessage(new MDSPingMessage {RepliedMessageId = e.Message.MessageId}, + e.Communicator); + } + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #endregion + + #region Processing MDSDataTransferMessage message + + /// + /// This method is used to process a MDSDataTransferMessage that is gotten from a mds server or client application. + /// Message is sent to destination or next server in one of these three conditions: + /// - Destination server is this server and application exists on this server + /// - Destination server is an adjacent server of this server + /// - Destination server in server graph and there is a path from this server to destination server + /// + /// Sender application/server + /// Sender communicator + /// Message + private void ProcessDataTransferMessage(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSDataTransferMessage message) + { + //Check for duplicate messages + if (senderApplication.LastAcknowledgedMessageId == message.MessageId) + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, true, "Duplicate message."); + return; + } + + try + { + AddThisServerToPassedServerList(message); + + FillEmptyMessageFields(message, senderApplication, senderCommunicator); + + _routingTable.ApplyRouting(message); + + //If Destination server is this server then deliver message to the destination application + if (message.DestinationServerName.Equals(_settings.ThisServerName, StringComparison.OrdinalIgnoreCase)) + { + SentToClientApplication(senderApplication, senderCommunicator, message); + } + //Else, if destination server is an adjacent of this server (so they can communicate directly) + else if (_serverGraph.AdjacentServers.ContainsKey(message.DestinationServerName)) + { + SentToAdjacentServer(senderApplication, senderCommunicator, message); + } + //Else, if destination server is not adjacent but in server graph (so, send message to next server) + else if (_serverGraph.ServerNodes.ContainsKey(message.DestinationServerName)) + { + SendToNextServer(senderApplication, senderCommunicator, message); + } + else + { + //return error to sender + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, "Destination does not exists."); + } + } + catch (Exception ex) + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, ex.Message); + } + } + + /// + /// Adds this server to the list of passed servers of message. + /// + /// Message object + private void AddThisServerToPassedServerList(MDSDataTransferMessage message) + { + //Create new transmit report for this server + var transmitReport = new ServerTransmitReport + { + ArrivingTime = DateTime.Now, + ServerName = _settings.ThisServerName + }; + if (message.PassedServers == null) + { + //Create array + message.PassedServers = new ServerTransmitReport[1]; + } + else + { + //Create new array (that has item one more than original array) + var newArray = new ServerTransmitReport[message.PassedServers.Length + 1]; + //Copy old items to new array + if (message.PassedServers.Length > 1) + { + Array.Copy(message.PassedServers, 0, newArray, 0, message.PassedServers.Length); + } + + //Replace old array by new array + message.PassedServers = newArray; + } + + //Add transmit report to array + message.PassedServers[message.PassedServers.Length - 1] = transmitReport; + } + + /// + /// Checks a MDSDataTransferMessage and fills it's empty fields by default values. + /// + /// Message + /// Sender application + /// Sender communicator of application + private void FillEmptyMessageFields(MDSDataTransferMessage dataTransferMessage, MDSRemoteApplication senderApplication, ICommunicator communicator) + { + //Default SourceApplicationName: Name of the sender application. + if (string.IsNullOrEmpty(dataTransferMessage.SourceApplicationName)) + { + dataTransferMessage.SourceApplicationName = senderApplication.Name; + } + + //Default SourceServerName: Name of this server. + if (string.IsNullOrEmpty(dataTransferMessage.SourceServerName)) + { + dataTransferMessage.SourceServerName = _settings.ThisServerName; + } + + //Default DestinationApplicationName: Name of the sender application. + if (string.IsNullOrEmpty(dataTransferMessage.DestinationApplicationName)) + { + dataTransferMessage.DestinationApplicationName = senderApplication.Name; + } + + //Default DestinationServerName: Name of this server. + if (string.IsNullOrEmpty(dataTransferMessage.DestinationServerName)) + { + dataTransferMessage.DestinationServerName = _settings.ThisServerName; + } + + if (dataTransferMessage.SourceServerName == _settings.ThisServerName) + { + //Sender communicator id is being set. + dataTransferMessage.SourceCommunicatorId = communicator.ComminicatorId; + } + } + + /// + /// This method is called by ProcessDataTransferMessage when a message must be sent to a aclient application + /// that is running on this server. + /// + /// Sender application/server + /// Sender communicator + /// Message + private void SentToClientApplication(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSDataTransferMessage message) + { + MDSClientApplication destinationApplication = null; + + //If application exists on this server, get it + lock (_clientApplicationList.Applications) + { + if (_clientApplicationList.Applications.ContainsKey(message.DestinationApplicationName)) + { + destinationApplication = _clientApplicationList.Applications[message.DestinationApplicationName]; + } + } + + //If application doesn't exist on this server... + if (destinationApplication == null) + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, "Application does not exists on this server (" + _settings.ThisServerName + ")."); + return; + } + + //Send message according TransmitRule + switch (message.TransmitRule) + { + case MessageTransmitRules.DirectlySend: + SendMessageDirectly( + senderApplication, + senderCommunicator, + destinationApplication, + message + ); + break; + default: + // case MessageTransmitRules.StoreAndForward: + // case MessageTransmitRules.NonPersistent: + EnqueueMessage( + senderApplication, + senderCommunicator, + destinationApplication, + message + ); + break; + } + } + + /// + /// This method is called by ProcessDataTransferMessage when a message must be sent to an adjacent server of this server. + /// + /// Sender application/server + /// Sender communicator + /// Message + private void SentToAdjacentServer(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSDataTransferMessage message) + { + /* On one of these conditions, message is stored: + * - TransmitRule = StoreAndForward + * - (TransmitRule = StoreOnSource OR StoreOnEndPoints) AND (This server is the source server) + */ + if (message.TransmitRule == MessageTransmitRules.StoreAndForward || + message.TransmitRule == MessageTransmitRules.NonPersistent) + { + EnqueueMessage( + senderApplication, + senderCommunicator, + _serverGraph.AdjacentServers[message.DestinationServerName], + message + ); + } + /* Else, message is not stored in these conditions: + * - TransmitRule = DirectlySend OR StoreOnDestination (this server can not be destination because message is being sent to another server right now) + * - All Other conditions + */ + else + { + SendMessageDirectly( + senderApplication, + senderCommunicator, + _serverGraph.AdjacentServers[message.DestinationServerName], + message + ); + } + } + + /// + /// This method is called by ProcessDataTransferMessage when a message must be sent to a server + /// that is not an adjacent of this server. Message is forwarded to next server. + /// + /// Sender application/server + /// Sender communicator + /// Message + private void SendToNextServer(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSDataTransferMessage message) + { + //If there is a path from this server to destination server... + if (_serverGraph.ThisServerNode.BestPathsToServers.ContainsKey(message.DestinationServerName)) + { + //Find best path to destination server + var bestPath = _serverGraph.ThisServerNode.BestPathsToServers[message.DestinationServerName]; + //If path is regular (a path must consist of 2 nodes at least)... + if (bestPath.Count > 1) + { + //Next server + var nextServerName = bestPath[1].Name; + + /* On one of these conditions, message is stored: + * - TransmitRule = StoreAndForward + * - (TransmitRule = StoreOnSource OR StoreOnEndPoints) AND (This server is the source server) + */ + if (message.TransmitRule == MessageTransmitRules.StoreAndForward || + message.TransmitRule == MessageTransmitRules.NonPersistent) + { + EnqueueMessage( + senderApplication, + senderCommunicator, + _serverGraph.AdjacentServers[nextServerName], + message + ); + } + /* Else, message is not stored in these conditions: + * - TransmitRule = DirectlySend OR StoreOnDestination (this server can not be destination because message is being sent to another server right now) + * - All Other conditions + */ + else + { + SendMessageDirectly( + senderApplication, + senderCommunicator, + _serverGraph.AdjacentServers[nextServerName], + message + ); + } + } + //Server graph may be wrong (this is just for checking case, normally this situation must not become) + else + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, "Server graph is wrong."); + } + } + //No path from this server to destination server + else + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, "There is no path from this server to destination."); + } + } + + /// + /// Sends message directly to application (not stores) and waits ACK. + /// This method adds message to queue by MDSPersistentRemoteApplicationBase.AddMessageToHeadOfQueue method + /// and waits a signal/pulse from RemoteApplication_MessageReceived method to get ACK/Reject. + /// + /// Sender application/server + /// Sender communicator + /// Destination application/server + /// Message + private void SendMessageDirectly(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSPersistentRemoteApplicationBase destApplication, MDSDataTransferMessage message) + { + //Create a WaitingMessage to wait and get ACK/Reject message and add it to waiting messages + var waitingMessage = new WaitingMessage(); + lock (_waitingMessages) + { + _waitingMessages[message.MessageId] = waitingMessage; + } + + try + { + //Add message to head of queue of remote application + destApplication.AddMessageToHeadOfQueue(message); + + //Wait until thread is signalled by another thread to get response (Signalled by RemoteApplication_MessageReceived method) + waitingMessage.WaitEvent.WaitOne((int) (_settings.MessageResponseTimeout*1.2)); + + //Evaluate response + if (waitingMessage.ResponseMessage.Success) + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, true, "Success."); + } + else + { + SendOperationResultMessage(senderApplication, senderCommunicator, message, false, "Message is not acknowledged. Reason: " + waitingMessage.ResponseMessage.ResultText); + } + } + finally + { + //Remove message from waiting messages + lock (_waitingMessages) + { + _waitingMessages.Remove(message.MessageId); + } + } + } + + /// + /// Adds message to destination's send queue. + /// + /// Sender application/server + /// Sender communicator + /// Destination application/server + /// Message + private static void EnqueueMessage(MDSRemoteApplication senderApplication, ICommunicator senderCommunicator, MDSPersistentRemoteApplicationBase destApplication, MDSDataTransferMessage message) + { + destApplication.EnqueueMessage(message); + SendOperationResultMessage(senderApplication, senderCommunicator, message, true, "Success."); + } + + /// + /// To send a MDSOperationResultMessage to remote application's spesific communicator. + /// + /// Sender application/server + /// Communicator to send message + /// Replied Message + /// Operation result + /// Details + private static void SendOperationResultMessage(MDSRemoteApplication senderApplication, ICommunicator communicator, MDSDataTransferMessage repliedMessage, bool success, string resultText) + { + try + { + if (success) + { + //Save MessageId of acknowledged message to do not receive same message again + senderApplication.LastAcknowledgedMessageId = repliedMessage.MessageId; + } + + senderApplication.SendMessage(new MDSOperationResultMessage + { + RepliedMessageId = repliedMessage.MessageId, + Success = success, + ResultText = resultText + }, communicator); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #endregion + + #region Sub classes + + /// + /// This class is used as item in _waitingMessages collection. + /// Key: Message ID to wait response. + /// Value: ManualResetEvent to wait thread until response received. + /// + /// + private class WaitingMessage + { + /// + /// ManualResetEvent to wait thread until response received. + /// + public ManualResetEvent WaitEvent { get; private set; } + + /// + /// Response message received as ACK/Reject for sent message + /// + public MDSOperationResultMessage ResponseMessage { get; set; } + + /// + /// Creates a new WaitingMessage. + /// + public WaitingMessage() + { + WaitEvent = new ManualResetEvent(false); + } + } + + #endregion + } +} diff --git a/src/MDSCore/Organization/Routing/DistributionStrategyBase.cs b/src/MDSCore/Organization/Routing/DistributionStrategyBase.cs new file mode 100644 index 0000000..d8efda0 --- /dev/null +++ b/src/MDSCore/Organization/Routing/DistributionStrategyBase.cs @@ -0,0 +1,67 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; +using MDS.Settings; + +namespace MDS.Organization.Routing +{ + /// + /// Base class for distribution strategies. + /// + internal abstract class DistributionStrategyBase + { + /// + /// Reference to RoutingRule object that uses this distribution strategy to route messages. + /// + protected readonly RoutingRule RoutingRule; + + /// + /// Constructor. + /// + /// Reference to RoutingRule object that uses this distribution strategy to route messages + protected DistributionStrategyBase(RoutingRule routingRule) + { + RoutingRule = routingRule; + } + + /// + /// Sets the destination of a message. + /// + /// Message to set it's destination + /// Destination to set to message + protected static void SetMessageDestination(MDSDataTransferMessage message, RoutingDestination destination) + { + //Sets destination server + if (!string.IsNullOrEmpty(destination.Server)) + { + message.DestinationServerName = destination.Server.Equals("this", StringComparison.OrdinalIgnoreCase) + ? MDSSettings.Instance.ThisServerName + : destination.Server; + } + + //Sets destination application + if (!string.IsNullOrEmpty(destination.Application)) + { + message.DestinationApplicationName = destination.Application; + } + } + } +} diff --git a/src/MDSCore/Organization/Routing/IDistributionStrategy.cs b/src/MDSCore/Organization/Routing/IDistributionStrategy.cs new file mode 100644 index 0000000..482ac58 --- /dev/null +++ b/src/MDSCore/Organization/Routing/IDistributionStrategy.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Organization.Routing +{ + /// + /// Interface of a distribution strategy. + /// A distribution strategy is a way of redirecting a message to one of available destinations. + /// + internal interface IDistributionStrategy + { + /// + /// Initializes and Resets distribution strategy. + /// + void Reset(); + + /// + /// Sets the destination of a message according to distribution strategy. + /// + /// Message to set it's destination + void SetDestination(MDSDataTransferMessage message); + } +} diff --git a/src/MDSCore/Organization/Routing/RandomDistribution.cs b/src/MDSCore/Organization/Routing/RandomDistribution.cs new file mode 100644 index 0000000..2c9366b --- /dev/null +++ b/src/MDSCore/Organization/Routing/RandomDistribution.cs @@ -0,0 +1,102 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; + +namespace MDS.Organization.Routing +{ + /// + /// Random distribution strategy. + /// According to this strategy, a message is routed one of available destinations randomly according to destination's RouteFactor. + /// For example, if, + /// + /// - Destination-A has a RouteFactor of 4 + /// - Destination-B has a RouteFactor of 1 + /// + /// Then, probability of routing a message to destinations: + /// + /// - Destination-A -> 80% + /// - Destination-B -> 20% + /// + internal class RandomDistribution : DistributionStrategyBase, IDistributionStrategy + { + /// + /// A Random object to create random numbers. + /// + private readonly Random _rnd; + + /// + /// Maximum count to create random numbers. + /// This is the total count of all RouteFactors of Destinations and calculated by Reset method. + /// + private int _maxCount; + + /// + /// Creates a new RandomDistribution object. + /// + /// Reference to RoutingRule object that uses this distribution strategy to route messages + public RandomDistribution(RoutingRule routingRule) + : base(routingRule) + { + _rnd = new Random(); + Reset(); + } + + /// + /// Initializes and Resets distribution strategy. + /// + public void Reset() + { + _maxCount = 0; + foreach (var destination in RoutingRule.Destinations) + { + _maxCount += destination.RouteFactor; + } + } + + /// + /// Sets the destination of a message according to distribution strategy. + /// + /// Message to set it's destination + public void SetDestination(MDSDataTransferMessage message) + { + //Return, if no destination exists + if (_maxCount == 0 || RoutingRule.Destinations.Length <= 0) + { + return; + } + + //Create a random number + var randomNumber = _rnd.Next(_maxCount); + + //Find destination according to random number and set the destination. + var currentTotal = 0; + foreach (var destination in RoutingRule.Destinations) + { + currentTotal += destination.RouteFactor; + if (randomNumber < currentTotal) + { + SetMessageDestination(message, destination); + return; + } + } + } + } +} diff --git a/src/MDSCore/Organization/Routing/RoutingDestination.cs b/src/MDSCore/Organization/Routing/RoutingDestination.cs new file mode 100644 index 0000000..f9ee280 --- /dev/null +++ b/src/MDSCore/Organization/Routing/RoutingDestination.cs @@ -0,0 +1,67 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Organization.Routing +{ + /// + /// Represents a Destination of a Routing. + /// + public class RoutingDestination + { + /// + /// Destination server name. Must be one of following values: + /// Empty string or null: Don't change destination server. + /// this: Change to this/current server. + /// A server name: Change to a specified server name. + /// + public string Server { get; set; } + + /// + /// Destination application name. Must be one of following values: + /// Empty string or null: Don't change destination application. + /// A application name: Change to a specified application name. + /// + public string Application { get; set; } + + /// + /// Route factor. + /// Must be 1 or greater. + /// + public int RouteFactor { get; set; } + + /// + /// Creates a new RoutingDestination object. + /// + public RoutingDestination() + { + Server = ""; + Application = ""; + RouteFactor = 1; + } + + /// + /// Returns a string that presents a brief information about RoutingFilter object. + /// + /// Brief information about RoutingFilter object + public override string ToString() + { + return string.Format("Server: {0}, Application: {1}, RouteFactor: {2}", Server, Application, RouteFactor); + } + } +} diff --git a/src/MDSCore/Organization/Routing/RoutingFilter.cs b/src/MDSCore/Organization/Routing/RoutingFilter.cs new file mode 100644 index 0000000..b964c50 --- /dev/null +++ b/src/MDSCore/Organization/Routing/RoutingFilter.cs @@ -0,0 +1,106 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Organization.Routing +{ + /// + /// Represents a filter of a routing. + /// + public class RoutingFilter + { + /// + /// Source server name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// this: For this/current server. + /// A server name: For a specified server name. + /// + public string SourceServer { get; set; } + + /// + /// Source application name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An application name: For a specified application name. + /// + public string SourceApplication { get; set; } + + /// + /// Destination server name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// this: For this/current server. + /// A server name: For a specified server name. + /// + public string DestinationServer { get; set; } + + /// + /// Destination application name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An application name: For a specified application name. + /// + public string DestinationApplication { get; set; } + + /// + /// Transmit rule for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An element of MessageTransmitRules enum. + /// + public string TransmitRule { get; set; } + + /// + /// Creates a new RoutingFilter object. + /// + public RoutingFilter() + { + SourceServer = ""; + SourceApplication = ""; + DestinationServer = ""; + DestinationApplication = ""; + TransmitRule = ""; + } + + /// + /// Checks if a message matches with this filter. + /// + /// Message to check + /// True, if message matches with this rule + public bool Matches(MDSDataTransferMessage message) + { + if ((string.IsNullOrEmpty(SourceServer) || SourceServer == message.SourceServerName) && + (string.IsNullOrEmpty(SourceApplication) || SourceApplication == message.SourceApplicationName) && + (string.IsNullOrEmpty(DestinationServer) || DestinationServer == message.DestinationServerName) && + (string.IsNullOrEmpty(DestinationApplication) || DestinationApplication == message.DestinationApplicationName) && + (string.IsNullOrEmpty(TransmitRule) || TransmitRule == message.TransmitRule.ToString())) + { + return true; + } + + return false; + } + + /// + /// Returns a string that presents a brief information about RoutingFilter object. + /// + /// Brief information about RoutingFilter object + public override string ToString() + { + return string.Format("SourceServer: {0}, SourceApplication: {1}, DestinationServer: {2}, DestinationApplication: {3}, TransmitRule: {4}", SourceServer, SourceApplication, DestinationServer, DestinationApplication, TransmitRule); + } + } +} diff --git a/src/MDSCore/Organization/Routing/RoutingRule.cs b/src/MDSCore/Organization/Routing/RoutingRule.cs new file mode 100644 index 0000000..324e8f2 --- /dev/null +++ b/src/MDSCore/Organization/Routing/RoutingRule.cs @@ -0,0 +1,108 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Organization.Routing +{ + /// + /// Represents a routing rule. + /// + public class RoutingRule + { + /// + /// Name of the Route. + /// It does not effect routing. + /// + public string Name { get; set; } + + /// + /// Gets/Sets Route distribution type. + /// Sequential: To use SequentialDistribution strategy. + /// Random: To use RandomDistribution strategy. + /// + public string DistributionType { get; set; } + + /// + /// Routing filters. + /// If a message matches one of this filters, it is routed by this RouteRule. + /// + public RoutingFilter[] Filters { get; set; } + + /// + /// Routing destinations. + /// A messages that is filtered by this rule is redirected one of this destinations according to current distribution strategy. + /// + public RoutingDestination[] Destinations { get; set; } + + /// + /// Gets the current distribution strategy. + /// + private IDistributionStrategy DistributionStrategy + { + get + { + if (_distributionStrategy == null) + { + switch (DistributionType) + { + case "Random": + _distributionStrategy = new RandomDistribution(this); + break; + //case "Sequential": + default: + _distributionStrategy = new SequentialDistribution(this); + break; + } + } + + return _distributionStrategy; + } + } + private IDistributionStrategy _distributionStrategy; + + /// + /// Tries to appliy this rule to a data transfer message. + /// + /// Message to apply rule + /// True, if rule applied + public bool ApplyRule(MDSDataTransferMessage message) + { + for (var i = 0; i < Filters.Length; i++) + { + if (Filters[i].Matches(message)) + { + DistributionStrategy.SetDestination(message); + return true; + } + } + + return false; + } + + /// + /// Returns a string that presents a brief information about RoutingRule object. + /// + /// Brief information about RoutingRule object + public override string ToString() + { + return string.Format("Rule: {0} (DistributionType: {1}, Filter Count: {2}, Destination Count: {3}).", Name, DistributionType, Filters.Length, Destinations.Length); + } + } +} diff --git a/src/MDSCore/Organization/Routing/RoutingTable.cs b/src/MDSCore/Organization/Routing/RoutingTable.cs new file mode 100644 index 0000000..516cdce --- /dev/null +++ b/src/MDSCore/Organization/Routing/RoutingTable.cs @@ -0,0 +1,115 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; +using System.Linq; +using MDS.Communication.Messages; +using MDS.Settings; + +namespace MDS.Organization.Routing +{ + /// + /// Represents routing table that contains all routing rules. + /// + public class RoutingTable + { + /// + /// All routing rules. + /// + public List Rules { get; private set; } + + /// + /// Settings. + /// + private readonly MDSSettings _settings; + + /// + /// Creates a new RoutingTable object. + /// + public RoutingTable() + { + _settings = MDSSettings.Instance; + Rules = new List(); + CreateRuleList(); + } + + /// + /// Checks all routing rules and apply proper rule to the message + /// + /// Message to apply routing + public void ApplyRouting(MDSDataTransferMessage message) + { + if (Rules.Any(rule => rule.ApplyRule(message))) + { + return; + } + } + + /// + /// Creates Rules list from settings. + /// + private void CreateRuleList() + { + foreach (var routeInfoItem in _settings.Routes) + { + var rule = new RoutingRule + { + Name = routeInfoItem.Name, + Filters = new RoutingFilter[routeInfoItem.Filters.Count], + Destinations = new RoutingDestination[routeInfoItem.Destinations.Count], + DistributionType = routeInfoItem.DistributionType + }; + + for (var i = 0; i < rule.Filters.Length; i++) + { + rule.Filters[i] = new RoutingFilter + { + SourceServer = routeInfoItem.Filters[i].SourceServer, + SourceApplication = routeInfoItem.Filters[i].SourceApplication, + DestinationServer = routeInfoItem.Filters[i].DestinationServer, + DestinationApplication = routeInfoItem.Filters[i].DestinationApplication, + TransmitRule = routeInfoItem.Filters[i].TransmitRule + }; + + if(rule.Filters[i].DestinationServer == "this") + { + rule.Filters[i].DestinationServer = MDSSettings.Instance.ThisServerName; + } + + if (rule.Filters[i].DestinationServer == "this") + { + rule.Filters[i].DestinationServer = MDSSettings.Instance.ThisServerName; + } + } + + for (var i = 0; i < rule.Destinations.Length; i++) + { + rule.Destinations[i] = new RoutingDestination + { + Server = routeInfoItem.Destinations[i].Server, + Application = routeInfoItem.Destinations[i].Application, + RouteFactor = routeInfoItem.Destinations[i].RouteFactor + }; + } + + Rules.Add(rule); + } + } + } +} diff --git a/src/MDSCore/Organization/Routing/SequentialDistribution.cs b/src/MDSCore/Organization/Routing/SequentialDistribution.cs new file mode 100644 index 0000000..6f15abb --- /dev/null +++ b/src/MDSCore/Organization/Routing/SequentialDistribution.cs @@ -0,0 +1,106 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using MDS.Communication.Messages; + +namespace MDS.Organization.Routing +{ + /// + /// Sequential distribution strategy. + /// According to this strategy, a message is routed one of available destinations sequentially according to destination's RouteFactor. + /// For example, if, + /// + /// - Destination-A has a RouteFactor of 4 + /// - Destination-B has a RouteFactor of 1 + /// + /// Then, 4 messages are sent to Destination-A, 1 message is sent to Destination-B sequentially. + /// + internal class SequentialDistribution : DistributionStrategyBase, IDistributionStrategy + { + /// + /// Total count of all RouteFactors of Destinations and calculated by Reset method. + /// + private int _totalCount; + + /// + /// Current routing number. It is used to determine the next routing destination. + /// For example, if, + /// + /// - Destination-A has a RouteFactor of 4 + /// - Destination-B has a RouteFactor of 3 + /// - Destination-C has a RouteFactor of 3 + /// + /// and _currentNumber is 5, then destination is Destination-B. + /// + private int _currentNumber; + + /// + /// Creates a new SequentialDistribution object. + /// + /// Reference to RoutingRule object that uses this distribution strategy to route messages + public SequentialDistribution(RoutingRule routingRule) + : base(routingRule) + { + Reset(); + } + + /// + /// Initializes and Resets distribution strategy. + /// + public void Reset() + { + _totalCount = 0; + foreach (var destination in RoutingRule.Destinations) + { + _totalCount += destination.RouteFactor; + } + } + + /// + /// Sets the destination of a message according to distribution strategy. + /// + /// Message to set it's destination + public void SetDestination(MDSDataTransferMessage message) + { + //Return, if no destination exists + if (_totalCount == 0 || RoutingRule.Destinations.Length <= 0) + { + return; + } + + //Find next destination and set + var currentTotal = 0; + foreach (var destination in RoutingRule.Destinations) + { + currentTotal += destination.RouteFactor; + if (_currentNumber < currentTotal) + { + SetMessageDestination(message, destination); + break; + } + } + + //Increase _currentNumber + if ((++_currentNumber) >= _totalCount) + { + _currentNumber = 0; + } + } + } +} diff --git a/src/MDSCore/Properties/AssemblyInfo.cs b/src/MDSCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e58604b --- /dev/null +++ b/src/MDSCore/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MDSCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("SESTEK A.Ş.")] +[assembly: AssemblyProduct("MDSCore")] +[assembly: AssemblyCopyright("Copyright © SESTEK A.Ş. 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: log4net.Config.XmlConfigurator(Watch = true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("692aef78-17a5-4c0e-a28a-e7b786e44b17")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.1.0")] +[assembly: AssemblyFileVersion("0.9.1.0")] diff --git a/src/MDSCore/Properties/Settings.Designer.cs b/src/MDSCore/Properties/Settings.Designer.cs new file mode 100644 index 0000000..612da5e --- /dev/null +++ b/src/MDSCore/Properties/Settings.Designer.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4952 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MDS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)] + [global::System.Configuration.DefaultSettingValueAttribute("http://localhost:2111/MDSWebServiceInterface/MDSAppService.asmx")] + public string MDSCore_MDSAppWebServiceRef_MDSAppService { + get { + return ((string)(this["MDSCore_MDSAppWebServiceRef_MDSAppService"])); + } + } + } +} diff --git a/src/MDSCore/Properties/Settings.settings b/src/MDSCore/Properties/Settings.settings new file mode 100644 index 0000000..6fb478c --- /dev/null +++ b/src/MDSCore/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + http://localhost:2111/MDSWebServiceInterface/MDSAppService.asmx + + + \ No newline at end of file diff --git a/src/MDSCore/Settings/ApplicationInfoItem.cs b/src/MDSCore/Settings/ApplicationInfoItem.cs new file mode 100644 index 0000000..f55a4d8 --- /dev/null +++ b/src/MDSCore/Settings/ApplicationInfoItem.cs @@ -0,0 +1,72 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace MDS.Settings +{ + /// + /// Represents a Client Application's informations in settings. + /// + public class ApplicationInfoItem + { + /// + /// Name of this server. + /// + public string Name { get; set; } + + /// + /// Predefined communication channels. + /// + public List CommunicationChannels { get; private set; } + + /// + /// Constructor. + /// + public ApplicationInfoItem() + { + CommunicationChannels = new List(); + } + + /// + /// Represents a predefined Communication channel for an Application. + /// + public class CommunicationChannelInfoItem + { + /// + /// Type of communicaton. Can be WebService. + /// + public string CommunicationType { get; set; } + + /// + /// Settings for communication. For example, includes Url info if CommunicationType is WebService. + /// + public NameValueCollection CommunicationSettings { get; set; } + + /// + /// Constructor. + /// + public CommunicationChannelInfoItem() + { + CommunicationSettings = new NameValueCollection(); + } + } + } +} diff --git a/src/MDSCore/Settings/MDSDesignSettings.cs b/src/MDSCore/Settings/MDSDesignSettings.cs new file mode 100644 index 0000000..d3c39a6 --- /dev/null +++ b/src/MDSCore/Settings/MDSDesignSettings.cs @@ -0,0 +1,174 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; +using System.IO; +using System.Xml; +using MDS.Exceptions; + +namespace MDS.Settings +{ + /// + /// This class is used to get/set all design settings for this server from/to an XML file. + /// Design settings is used by MDS Manager GUI. + /// + public class MDSDesignSettings + { + #region Static properties + + /// + /// Singleton instance of MDSSettings. + /// + public static MDSDesignSettings Instance + { + get + { + if (_instance == null) + { + lock (_synObj) + { + if (_instance == null) + { + _instance = new MDSDesignSettings(Path.Combine(GeneralHelper.GetCurrentDirectory(), "MDSSettings.design.xml")); + } + } + } + + return _instance; + } + + set + { + lock (_synObj) + { + _instance = value; + } + } + } + + private static MDSDesignSettings _instance; + + private static readonly object _synObj = new object(); + + #endregion + + #region Public properties + + /// + /// All defined MDS servers in server graph. + /// + public List Servers { get; private set; } + + /// + /// Path of XML design settings file. + /// + public string FilePath { get; private set; } + + #endregion + + #region Constructor + + /// + /// Creates a new MDSSettings from XML file. + /// + /// Path of xml file. + public MDSDesignSettings(string settingsFilePath) + { + FilePath = settingsFilePath; + Servers = new List(); + LoadFromXml(); + } + + #endregion + + #region Public methods + + /// + /// Saves current design settings to XML file. + /// + public void SaveToXml() + { + //Create directory if needed + var saveDirectory = Path.GetDirectoryName(FilePath); + if (!Directory.Exists(saveDirectory)) + { + Directory.CreateDirectory(saveDirectory); + } + + //Create XmlDocument object to create XML file + var xmlDoc = new XmlDocument(); + + //XML declaration + var xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null); + xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement); + + //Root node + var rootNode = xmlDoc.CreateElement("MDSConfiguration"); + xmlDoc.AppendChild(rootNode); + + //Servers node + var serversRootNode = xmlDoc.CreateElement("Servers"); + rootNode.AppendChild(serversRootNode); + + //Server nodes + foreach (var server in Servers) + { + var serverNode = xmlDoc.CreateElement("Server"); + serverNode.SetAttribute("Name", server.Name); + serverNode.SetAttribute("Location", server.Location); + serversRootNode.AppendChild(serverNode); + } + + //Save XML document + xmlDoc.Save(FilePath); + } + + #endregion + + #region Private methods + + /// + /// Gets all settings from XML file. + /// + private void LoadFromXml() + { + //Create XmlDocument object to read XML settings file + var settingsXmlDoc = new XmlDocument(); + settingsXmlDoc.Load(FilePath); + + //Get Servers section + var resultNodes = settingsXmlDoc.SelectNodes("/MDSConfiguration/Servers/Server"); + if (resultNodes == null) + { + throw new MDSException("No server defined"); + } + + foreach (XmlNode node in resultNodes) + { + Servers.Add(new ServerDesignItem + { + Name = node.Attributes["Name"].Value.Trim(), + Location = node.Attributes["Location"].Value + }); + } + } + + #endregion + } +} diff --git a/src/MDSCore/Settings/MDSSettings.cs b/src/MDSCore/Settings/MDSSettings.cs new file mode 100644 index 0000000..d6b2202 --- /dev/null +++ b/src/MDSCore/Settings/MDSSettings.cs @@ -0,0 +1,399 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.IO; +using System.Xml; +using MDS.Exceptions; + +namespace MDS.Settings +{ + /// + /// This class is used to get/set all settings for this server from/to an XML file. + /// + public class MDSSettings + { + #region Static properties + + /// + /// Singleton instance of MDSSettings. + /// + public static MDSSettings Instance + { + get + { + if(_instance == null) + { + lock(_synObj) + { + if (_instance == null) + { + _instance = new MDSSettings(Path.Combine(GeneralHelper.GetCurrentDirectory(), "MDSSettings.xml")); + } + } + } + + return _instance; + } + + set + { + lock (_synObj) + { + _instance = value; + } + } + } + + private static MDSSettings _instance; + + private static readonly object _synObj = new object(); + + #endregion + + #region Public properties + + /// + /// All defined MDS servers in server graph. + /// + public List Servers { get; private set; } + + /// + /// All defined Client applications. + /// + public List Applications { get; private set; } + + /// + /// All defined Route informations. + /// + public List Routes { get; private set; } + + /// + /// Name of this server. + /// + public string ThisServerName + { + get + { + return _thisServerName; + } + + set + { + _thisServerName = value; + _settings["ThisServerName"] = _thisServerName; + } + } + private string _thisServerName; + + /// + /// This value indicates timeout value to wait for ACK/Reject message from a remote application for a data transfer message. + /// + public int MessageResponseTimeout { get; set; } + + /// + /// Gets/Sets a setting. + /// + /// Name of setting + /// Value of setting + public string this[string fieldName] + { + get { return _settings[fieldName]; } + set { _settings[fieldName] = value; } + } + private readonly NameValueCollection _settings; + + /// + /// Path of XML settings file. + /// + public string FilePath { get; private set; } + + #endregion + + #region Constructor + + /// + /// Creates a new MDSSettings from XML file. + /// + /// Path of xml file. + public MDSSettings(string settingsFilePath) + { + FilePath = settingsFilePath; + MessageResponseTimeout = 300000; //5 minutes + Servers = new List(); + Applications = new List(); + Routes = new List(); + _settings = new NameValueCollection(); + LoadFromXml(); + } + + #endregion + + #region Public methods + + /// + /// Saves current settings to XML file. + /// + public void SaveToXml() + { + //Create directory if needed + var saveDirectory = Path.GetDirectoryName(FilePath); + if (!Directory.Exists(saveDirectory)) + { + Directory.CreateDirectory(saveDirectory); + } + + //Create XmlDocument object to create XML file + var xmlDoc = new XmlDocument(); + + //XML declaration + var xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null); + xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement); + + //Root node + var rootNode = xmlDoc.CreateElement("MDSConfiguration"); + xmlDoc.AppendChild(rootNode); + + //Settings node + var settingsRootNode = xmlDoc.CreateElement("Settings"); + rootNode.AppendChild(settingsRootNode); + + //Setting nodes + foreach (var key in _settings.AllKeys) + { + if (key.StartsWith("__")) + { + continue; + } + + var settingNode = xmlDoc.CreateElement("Setting"); + settingNode.SetAttribute("Key", key); + settingNode.SetAttribute("Value", _settings[key]); + settingsRootNode.AppendChild(settingNode); + } + + //Servers node + var serversRootNode = xmlDoc.CreateElement("Servers"); + rootNode.AppendChild(serversRootNode); + + //Server nodes + foreach (var server in Servers) + { + var serverNode = xmlDoc.CreateElement("Server"); + serverNode.SetAttribute("Name", server.Name); + serverNode.SetAttribute("IpAddress", server.IpAddress); + serverNode.SetAttribute("Port", server.Port.ToString()); + serverNode.SetAttribute("Adjacents", server.Adjacents); + serversRootNode.AppendChild(serverNode); + } + + //Applications node + var applicationRootNode = xmlDoc.CreateElement("Applications"); + rootNode.AppendChild(applicationRootNode); + + //Application nodes + foreach (var application in Applications) + { + var applicationNode = xmlDoc.CreateElement("Application"); + applicationNode.SetAttribute("Name", application.Name); + applicationRootNode.AppendChild(applicationNode); + + foreach (var channel in application.CommunicationChannels) + { + var channelNode = xmlDoc.CreateElement("Communication"); + channelNode.SetAttribute("Type", channel.CommunicationType); + switch (channel.CommunicationType) + { + case "WebService": + channelNode.SetAttribute("Url", channel.CommunicationSettings["Url"]); + break; + } + + applicationNode.AppendChild(channelNode); + } + } + + //Save XML document + xmlDoc.Save(FilePath); + } + + #endregion + + #region Private methods + + /// + /// Gets all settings from XML file. + /// + private void LoadFromXml() + { + //Create XmlDocument object to read XML settings file + var settingsXmlDoc = new XmlDocument(); + settingsXmlDoc.Load(FilePath); + + //Get Settings section + var resultNodes = settingsXmlDoc.SelectNodes("/MDSConfiguration/Settings/Setting"); + if (resultNodes == null) + { + return; + } + + foreach (XmlNode node in resultNodes) + { + _settings[node.Attributes["Key"].Value] = node.Attributes["Value"].Value; + } + + if (string.IsNullOrEmpty(_settings["ThisServerName"])) + { + throw new MDSException("ThisServerName is not defined."); + } + + _thisServerName = _settings["ThisServerName"]; + + //Get Servers section + resultNodes = settingsXmlDoc.SelectNodes("/MDSConfiguration/Servers/Server"); + if (resultNodes == null) + { + throw new MDSException("No server defined."); + } + + foreach (XmlNode node in resultNodes) + { + Servers.Add(new ServerInfoItem + { + Name = node.Attributes["Name"].Value.Trim(), + IpAddress = node.Attributes["IpAddress"].Value, + Port = Convert.ToInt32(node.Attributes["Port"].Value), + Adjacents = node.Attributes["Adjacents"].Value + }); + } + + //Get Applications section + resultNodes = settingsXmlDoc.SelectNodes("/MDSConfiguration/Applications/Application"); + if (resultNodes != null) + { + //Read all application entries from xml file + foreach (XmlNode node in resultNodes) + { + var application = new ApplicationInfoItem + { + Name = node.Attributes["Name"].Value + }; + + //Add predefined communication channels + foreach (XmlNode childNode in node.ChildNodes) + { + switch (childNode.Name) + { + case "Communication": + switch (childNode.Attributes["Type"].Value) + { + case "WebService": + var webServiceComm = new ApplicationInfoItem.CommunicationChannelInfoItem + { + CommunicationType = "WebService" + }; + webServiceComm.CommunicationSettings.Add("Url", childNode.Attributes["Url"].Value); + application.CommunicationChannels.Add(webServiceComm); + break; + } + + break; + } + } + + Applications.Add(application); + } + } + + //Get Routes section + resultNodes = settingsXmlDoc.SelectNodes("/MDSConfiguration/Routes/Route"); + if (resultNodes != null) + { + //Read all route entries from xml file + foreach (XmlNode node in resultNodes) + { + var route = new RouteInfoItem + { + Name = node.Attributes["Name"].Value, + DistributionType = GetAttribute(node, "DistributionType") + }; + + //Read all filter entries of route from xml file + var filterNodes = node.SelectNodes("/MDSConfiguration/Routes/Route/Filters/Filter"); + if (filterNodes != null) + { + foreach (XmlNode filterNode in filterNodes) + { + var filter = new RouteInfoItem.FilterInfoItem + { + SourceServer = GetAttribute(filterNode, "SourceServer"), + SourceApplication = GetAttribute(filterNode, "SourceApplication"), + DestinationServer = GetAttribute(filterNode, "DestinationServer"), + DestinationApplication = + GetAttribute(filterNode, "DestinationApplication"), + TransmitRule = GetAttribute(filterNode, "TransmitRule") + }; + route.Filters.Add(filter); + } + } + + //Read all destination entries of route from xml file + var destinationNodes = node.SelectNodes("/MDSConfiguration/Routes/Route/Destinations/Destination"); + if (destinationNodes != null) + { + foreach (XmlNode destinationNode in destinationNodes) + { + var destination = new RouteInfoItem.DestinationInfoItem + { + Application = GetAttribute(destinationNode, "Application"), + Server = GetAttribute(destinationNode, "Server"), + RouteFactor = Convert.ToInt32(GetAttribute(destinationNode, "RouteFactor") ?? "1") + }; + if (destination.RouteFactor <= 0) + { + destination.RouteFactor = 1; + } + + route.Destinations.Add(destination); + } + } + + Routes.Add(route); + } + } + } + + private static string GetAttribute(XmlNode node, string attributeName) + { + foreach (XmlAttribute attribute in node.Attributes) + { + if(attribute.Name == attributeName) + { + return attribute.Value; + } + } + + return null; + } + + #endregion + } +} diff --git a/src/MDSCore/Settings/RouteInfoItem.cs b/src/MDSCore/Settings/RouteInfoItem.cs new file mode 100644 index 0000000..6c0e052 --- /dev/null +++ b/src/MDSCore/Settings/RouteInfoItem.cs @@ -0,0 +1,133 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; + +namespace MDS.Settings +{ + /// + /// Represents a Route information in settings. + /// + public class RouteInfoItem + { + /// + /// Name of the Route. + /// It does not effect routing. + /// + public string Name { get; set; } + + /// + /// Distribution type/strategy. Must be one of following values: + /// Sequential: To use Sequential Distribution strategy. + /// Random: To use Random Distribution strategy. + /// + public string DistributionType { get; set; } + + /// + /// Routing filters. + /// If a message matches one of this filters, it is routed by this route rule. + /// + public List Filters { get; private set; } + + /// + /// Routing destinations. + /// A messages that is filtered by this rule is redirected one of this destinations according to current distribution strategy. + /// + public List Destinations { get; private set; } + + /// + /// Creates a new RouteInfoItem object. + /// + public RouteInfoItem() + { + Filters = new List(); + Destinations = new List(); + } + + /// + /// Represents a Filter information of a Route in settings. + /// + public class FilterInfoItem + { + /// + /// Source server name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// this: For this/current server. + /// A server name: For a specified server name. + /// + public string SourceServer { get; set; } + + /// + /// Source application name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An application name: For a specified application name. + /// + public string SourceApplication { get; set; } + + /// + /// Destination server name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// this: For this/current server. + /// A server name: For a specified server name. + /// + public string DestinationServer { get; set; } + + /// + /// Destination application name for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An application name: For a specified application name. + /// + public string DestinationApplication { get; set; } + + /// + /// Transmit rule for the filter. Must be one of following values: + /// Empty string or null: No filter on this property. + /// An element of MessageTransmitRules enum. + /// + public string TransmitRule { get; set; } + } + + /// + /// Represents a Destination information of a Route in settings. + /// + public class DestinationInfoItem + { + /// + /// Destination server name. Must be one of following values: + /// Empty string or null: Don't change destination server. + /// this: Change to this/current server. + /// A server name: Change to a specified server name. + /// + public string Server { get; set; } + + /// + /// Destination application name. Must be one of following values: + /// Empty string or null: Don't change destination application. + /// A application name: Change to a specified application name. + /// + public string Application { get; set; } + + /// + /// Route factor. + /// Must be 1 or greater. + /// + public int RouteFactor { get; set; } + } + } +} diff --git a/src/MDSCore/Settings/ServerDesignItem.cs b/src/MDSCore/Settings/ServerDesignItem.cs new file mode 100644 index 0000000..7cd87a2 --- /dev/null +++ b/src/MDSCore/Settings/ServerDesignItem.cs @@ -0,0 +1,37 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Settings +{ + /// + /// Represents a Server's design informations in design settings. + /// + public class ServerDesignItem + { + /// + /// Name of this server. + /// + public string Name { get; set; } + + /// + /// Location of server (Left (X) and Top (Y) properties in design area, seperated by comma (,)). + /// + public string Location { get; set; } + } +} diff --git a/src/MDSCore/Settings/ServerInfoItem.cs b/src/MDSCore/Settings/ServerInfoItem.cs new file mode 100644 index 0000000..0fe2d76 --- /dev/null +++ b/src/MDSCore/Settings/ServerInfoItem.cs @@ -0,0 +1,47 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace MDS.Settings +{ + /// + /// Represents a Server's informations in settings. + /// + public class ServerInfoItem + { + /// + /// Name of this server. + /// + public string Name { get; set; } + + /// + /// IP address of this server. + /// + public string IpAddress { get; set; } + + /// + /// TCP Port number that is listened by this server. + /// + public int Port { get; set; } + + /// + /// List of adjacent servers of this server that are splitted by , or ; + /// + public string Adjacents { get; set; } + } +} diff --git a/src/MDSCore/Storage/FaultToleratedStorageManagerWrapper.cs b/src/MDSCore/Storage/FaultToleratedStorageManagerWrapper.cs new file mode 100644 index 0000000..c3bb423 --- /dev/null +++ b/src/MDSCore/Storage/FaultToleratedStorageManagerWrapper.cs @@ -0,0 +1,375 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using MDS.Exceptions; + +namespace MDS.Storage +{ + /// + /// This class adds fault tolerance improvements to a Storage Manager. + /// Tries database operations more than once until specified timeout occurs. + /// + public class FaultToleratedStorageManagerWrapper : IStorageManager + { + #region Public properties + + /// + /// Timeout value to cancel trying operation again (as milliseconds). + /// Default: 90 seconds (90000 ms). + /// + public int TimeOut { get; set; } + + /// + /// This value determines th time to wait before retry on an exception (as milliseconds). + /// Default: 1 seconds (1000 ms). + /// + public int WaitTimeBeforeRetry { get; set; } + + /// + /// If this is true, StorageManager is restarted (Stop, Start) after an error, before next try. + /// Default: true + /// + public bool RestartStorageManagerOnException { get; set; } + + #endregion + + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Original Storage Manager to perform database operations. + /// + private readonly IStorageManager _storageManager; + + /// + /// The last restart time of storage manager. + /// This is used to prevent very frequently restarts. + /// + private DateTime _lastRestartTime; + + /// + /// This object is used to synchronizing threads. + /// + private readonly object _syncObj = new object(); + + #endregion + + #region Constructors + + /// + /// Creates a new FaultToleratedStorageManagerWrapper, wraps a IStorageManager. + /// + /// Original Storage Manager to perform database operations + public FaultToleratedStorageManagerWrapper(IStorageManager storageManager) + { + if (storageManager == null) + { + throw new ArgumentNullException("storageManager", "storageManager parameter can not be null."); + } + + _storageManager = storageManager; + _lastRestartTime = DateTime.MinValue; + TimeOut = 90000; + WaitTimeBeforeRetry = 1000; + RestartStorageManagerOnException = true; + } + + #endregion + + #region IStorageManager members + + public void Start() + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + _storageManager.Start(); + return; + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public void Stop(bool waitToStop) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + _storageManager.Stop(waitToStop); + return; + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public void WaitToStop() + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + _storageManager.WaitToStop(); + return; + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public int StoreMessage(MDSMessageRecord messageRecord) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.StoreMessage(messageRecord); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.GetWaitingMessagesOfApplication(nextServer, destApplication, minId, maxCount); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.GetMaxWaitingMessageIdOfApplication(nextServer, destApplication); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.GetWaitingMessagesOfServer(nextServer, minId, maxCount); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.GetMaxWaitingMessageIdOfServer(nextServer); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public int RemoveMessage(int id) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + return _storageManager.RemoveMessage(id); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + public void UpdateNextServer(string destServer, string nextServer) + { + var startTime = DateTime.Now; + Exception lastException = null; + while (DateTime.Now.Subtract(startTime).TotalMilliseconds < TimeOut) + { + try + { + _storageManager.UpdateNextServer(destServer, nextServer); + return; + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + lastException = ex; + Thread.Sleep(WaitTimeBeforeRetry); + RestartStorageManager(); + } + } + + throw new MDSDatabaseException("Can not performed a database operation.", lastException); + } + + #endregion + + #region Private methods + + /// + /// Restarts Storage Manager if RestartStorageManagerOnException is true. + /// + private void RestartStorageManager() + { + if (!RestartStorageManagerOnException) + { + return; + } + + //Locked _syncObj to make restart by only one thread in a time + lock (_syncObj) + { + //Do not restart if already restarted in last 1 minute + if(DateTime.Now.Subtract(_lastRestartTime).TotalSeconds < 60) + { + return; + } + + try + { + _storageManager.Stop(true); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + + try + { + _storageManager.Start(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + + //Update last restart time + _lastRestartTime = DateTime.Now; + } + } + + #endregion + } +} diff --git a/src/MDSCore/Storage/IStorageManager.cs b/src/MDSCore/Storage/IStorageManager.cs new file mode 100644 index 0000000..9d91cf4 --- /dev/null +++ b/src/MDSCore/Storage/IStorageManager.cs @@ -0,0 +1,87 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; +using MDS.Threading; + +namespace MDS.Storage +{ + /// + /// Defines an interface for (database) storing operations. Thus, MDS Server can use more than one + /// storage engine for messages and other database operations. + /// + public interface IStorageManager : IRunnable + { + /// + /// Saves a MDSMessageRecord. + /// + /// MDSMessageRecord object to save + /// Auto Increment Id of saved message + int StoreMessage(MDSMessageRecord messageRecord); + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount); + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication); + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount); + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + int GetMaxWaitingMessageIdOfServer(string nextServer); + + /// + /// Removes a message. + /// + /// id of message to remove + /// Effected rows count + int RemoveMessage(int id); + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + void UpdateNextServer(string destServer, string nextServer); + } +} diff --git a/src/MDSCore/Storage/MDSMessageRecord.cs b/src/MDSCore/Storage/MDSMessageRecord.cs new file mode 100644 index 0000000..dff7b24 --- /dev/null +++ b/src/MDSCore/Storage/MDSMessageRecord.cs @@ -0,0 +1,86 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using MDS.Communication.Messages; + +namespace MDS.Storage +{ + /// + /// Represents a message record in database/storage manager. + /// + public class MDSMessageRecord + { + /// + /// Auto Increment ID in database. + /// + public int Id { get; set; } + + /// + /// MessageId of message. + /// + public string MessageId { get; set; } + + /// + /// Message object. + /// + public MDSDataTransferMessage Message { get; set; } + + /// + /// Destination server. + /// + public string DestServer { get; set; } + + /// + /// Next server. + /// + public string NextServer { get; set; } + + /// + /// Destination application in destination server + /// + public string DestApplication { get; set; } + + /// + /// Storing time of message on this server. + /// + public DateTime RecordDate { get; set; } + + /// + /// Empty contructor. + /// + public MDSMessageRecord() + { + + } + + /// + /// Creates a MDSMessageRecord object using a MDSDataTransferMessage. + /// + /// Message object + public MDSMessageRecord(MDSDataTransferMessage message) + { + Message = message; + MessageId = message.MessageId; + DestServer = message.DestinationServerName; + DestApplication = message.DestinationApplicationName; + RecordDate = DateTime.Now; + } + } +} diff --git a/src/MDSCore/Storage/MemoryStorage/MemoryStorageManager.cs b/src/MDSCore/Storage/MemoryStorage/MemoryStorageManager.cs new file mode 100644 index 0000000..5018685 --- /dev/null +++ b/src/MDSCore/Storage/MemoryStorage/MemoryStorageManager.cs @@ -0,0 +1,227 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; + +namespace MDS.Storage.MemoryStorage +{ + /// + /// Performs all database/storing operations on memory. So, all stored informations are lost + /// on server shutdown. + /// + public class MemoryStorageManager : IStorageManager + { + /// + /// A list to store message records (Messages table). + /// + private readonly SortedList _messages; + + /// + /// Stores ID field of last inserted item to the _messages list. + /// + private volatile int _lastRecordId; + + /// + /// Creates a new MemoryStorageManager. + /// + public MemoryStorageManager() + { + _messages = new SortedList(); + } + + /// + /// Starts the storage manager. + /// + public void Start() + { + //No action + } + + /// + /// Stops the storage manager. + /// + public void Stop(bool waitToStop) + { + _messages.Clear(); + } + + public void WaitToStop() + { + //No action + } + + /// + /// Saves a MDSMessageRecord object. + /// + /// + public int StoreMessage(MDSMessageRecord messageRecord) + { + lock (_messages) + { + messageRecord.Id = (++_lastRecordId); + _messages.Add(messageRecord.Id, messageRecord); + return messageRecord.Id; + } + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + var results = new List(); + lock (_messages) + { + foreach (var record in _messages.Values) + { + if (record.NextServer.Equals(nextServer) && record.DestApplication.Equals(destApplication) + && record.Id >= minId) + { + results.Add(record); + if (results.Count >= maxCount) + { + break; + } + } + } + } + + return results; + } + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + lock (_messages) + { + var messageRecords = _messages.Values; + for (var i = messageRecords.Count - 1; i >= 0; i--) + { + var record = messageRecords[i]; + if (record.NextServer.Equals(nextServer) && record.DestApplication.Equals(destApplication)) + { + return record.Id; + } + } + } + + return 0; + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + var results = new List(); + lock (_messages) + { + foreach (var record in _messages.Values) + { + if (record.NextServer.Equals(nextServer) && record.Id >= minId) + { + results.Add(record); + if (results.Count >= maxCount) + { + break; + } + } + } + } + + return results; + } + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + lock (_messages) + { + var messageRecords = _messages.Values; + for (var i = messageRecords.Count - 1; i >= 0; i--) + { + var record = messageRecords[i]; + if (record.NextServer.Equals(nextServer)) + { + return record.Id; + } + } + } + + return 0; + } + + /// + /// Removes a message record. + /// + /// recordId to delete + /// Effected rows count + public int RemoveMessage(int recordId) + { + lock (_messages) + { + if(_messages.ContainsKey(recordId)) + { + _messages.Remove(recordId); + return 1; + } + } + + return 0; + } + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + public void UpdateNextServer(string destServer, string nextServer) + { + lock (_messages) + { + foreach (var record in _messages.Values) + { + if (record.DestServer.Equals(nextServer)) + { + record.NextServer = nextServer; + } + } + } + } + } +} diff --git a/src/MDSCore/Storage/MsSqlStorage/MsSqlStorageManager.cs b/src/MDSCore/Storage/MsSqlStorage/MsSqlStorageManager.cs new file mode 100644 index 0000000..2a1257c --- /dev/null +++ b/src/MDSCore/Storage/MsSqlStorage/MsSqlStorageManager.cs @@ -0,0 +1,380 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Data; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Serialization; +using System.Data.SqlClient; + +namespace MDS.Storage.MsSqlStorage +{ + /// + /// This class is used to perform database operations on MS SQL Server database engine. + /// + public class MsSqlStorageManager : IStorageManager + { + #region Private fields + + /// + /// Connection string to connect database. + /// + public string ConnectionString + { + get { return _connectionString; } + set { _connectionString = value; } + } + private string _connectionString = "Data Source=localhost;Initial Catalog=mds;Integrated Security=SSPI;"; + + #endregion + + #region Public methods + + #region Unimplemented methods + + public void Start() + { + //No action + } + + public void Stop(bool waitToStop) + { + //No action + } + + public void WaitToStop() + { + //No action + } + + #endregion + + /// + /// Saves a MDSMessageRecord. + /// + /// MDSMessageRecord object to save + /// Auto Increment Id of saved message + public int StoreMessage(MDSMessageRecord messageRecord) + { + var bytesOfMessage = MDSSerializationHelper.SerializeToByteArray(messageRecord.Message); + var id = InsertAndGetLastId( + "INSERT INTO Messages(MessageId, DestServer, NextServer, DestApplication, MessageData, MessageDataLength, RecordDate) VALUES(@MessageId, @DestServer, @NextServer, @DestApplication, @MessageData, @MessageDataLength, GetDate())", + new SqlParameter("@MessageId", messageRecord.MessageId), + new SqlParameter("@DestServer", messageRecord.DestServer), + new SqlParameter("@NextServer", messageRecord.NextServer), + new SqlParameter("@DestApplication", messageRecord.DestApplication), + new SqlParameter("@MessageData", bytesOfMessage), + new SqlParameter("@MessageDataLength", bytesOfMessage.Length) + ); + messageRecord.Id = id; + return id; + + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT TOP " + maxCount + " * FROM Messages WHERE NextServer = @NextServer AND DestApplication = @DestApplication AND Id >= @Id ORDER BY Id ASC", + new SqlParameter("@NextServer", nextServer), + new SqlParameter("@DestApplication", destApplication), + new SqlParameter("@Id", minId) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[]) recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime) recordRow["RecordDate"] + }); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + return GetScalarField( + "SELECT TOP 1 Id FROM Messages WHERE NextServer = @NextServer AND DestApplication = @DestApplication ORDER BY Id DESC", + new SqlParameter("@NextServer", nextServer), + new SqlParameter("@DestApplication", destApplication) + ); + } + + /// + /// Gets waiting messages for an MDS server. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT TOP " + maxCount + " * FROM Messages WHERE NextServer = @NextServer AND Id >= @Id ORDER BY Id ASC", + new SqlParameter("@NextServer", nextServer), + new SqlParameter("@Id", minId) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[]) recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime) recordRow["RecordDate"] + }); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + return GetScalarField( + "SELECT TOP 1 Id FROM Messages WHERE NextServer = @NextServer ORDER BY Id DESC", + new SqlParameter("@NextServer", nextServer) + ); + } + + /// + /// Removes a message. + /// + /// id of message to remove + /// Effected rows count + public int RemoveMessage(int id) + { + return ExecuteNonQuery( + "DELETE FROM Messages WHERE Id = @Id", + new SqlParameter("@Id", id) + ); + } + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + public void UpdateNextServer(string destServer, string nextServer) + { + ExecuteNonQuery( + "UPDATE Messages SET NextServer = @NextServer WHERE DestServer = @DestServer", + new SqlParameter("@NextServer", nextServer), + new SqlParameter("@DestServer", destServer) + ); + } + + #endregion + + #region Helper Methods + + /// + /// Executes a query and returns effected rows count. + /// + /// Query to execute + /// Parameters + /// Effected rows count + private int ExecuteNonQuery(string query, params SqlParameter[] parameters) + { + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new SqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + return command.ExecuteNonQuery(); + } + } + } + + /// + /// This method is used to run an insert query and get inserted row's auto increment column's value. + /// + /// Insert query to be executed + /// Parameters + /// Auto increment column's value of inserted row + private int InsertAndGetLastId(string query, params SqlParameter[] parameters) + { + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new SqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + command.ExecuteNonQuery(); + } + + const string queryForLastId = "SELECT @@Identity AS LastId;"; + using (var command = new SqlCommand(queryForLastId, connection)) + { + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader["LastId"]); + } + + throw new MDSException("Can not be obtained last inserted id for query: " + query); + } + } + } + } + + /// + /// Runs a query and returns a DataTable. + /// + /// Query to execute + /// Parameters + /// Selected table + private DataTable GetTable(string query, params SqlParameter[] parameters) + { + var table = new DataTable(); + using (var connection = new SqlConnection(ConnectionString)) + { + using (var command = new SqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var adapter = new SqlDataAdapter(command)) + { + adapter.Fill(table); + return table; + } + } + } + } + + /// + /// Gets a record from a table. + /// + /// Select query + /// Select parameters + /// Returns found row as TableRecord object. If there is no row returns null + public TableRecord GetTableRecord(string query, params SqlParameter[] parameters) + { + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new SqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + var record = new TableRecord(); + for (var i = 0; i < reader.FieldCount; i++) + { + record[reader.GetName(i)] = reader[i]; + } + + return record; + } + + return null; + } + } + } + } + + /// + /// Executes a query and gets a Integer result. + /// If query returns no data, method returns 0. + /// + /// Query to execute + /// Parameters + /// Query result or 0 + public int GetScalarField(string query, params SqlParameter[] parameters) + { + using (var connection = new SqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new SqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader[0]); + } + + return 0; + } + } + } + } + + #endregion + } +} diff --git a/src/MDSCore/Storage/MySqlStorage/MySqlNetStorageManager.cs b/src/MDSCore/Storage/MySqlStorage/MySqlNetStorageManager.cs new file mode 100644 index 0000000..fcc3c10 --- /dev/null +++ b/src/MDSCore/Storage/MySqlStorage/MySqlNetStorageManager.cs @@ -0,0 +1,384 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Data; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Serialization; +using MySql.Data.MySqlClient; + +namespace MDS.Storage.MySqlStorage +{ + /// + /// This class is used to perform database operations on MySQL database engine using MySQL .Net Provider. + /// + public class MySqlNetStorageManager : IStorageManager + { + #region Private fields + + /// + /// Connection string to connect database. + /// + public string ConnectionString + { + get { return _connectionString; } + set { _connectionString = value; } + } + private string _connectionString = "Server=127.0.0.1; Database=mds; Uid=root; Pooling=true; CharSet=latin5;"; + + #endregion + + #region Public methods + + #region Unimplemented methods + + public void Start() + { + //No action + } + + public void Stop(bool waitToStop) + { + //No action + } + + public void WaitToStop() + { + //No action + } + + #endregion + + /// + /// Saves a MDSMessageRecord. + /// + /// MDSMessageRecord object to save + /// Auto Increment Id of saved message + public int StoreMessage(MDSMessageRecord messageRecord) + { + var bytesOfMessage = MDSSerializationHelper.SerializeToByteArray(messageRecord.Message); + var id = InsertAndGetLastId( + "INSERT INTO messages(MessageId, DestServer, NextServer, DestApplication, MessageData, MessageDataLength, RecordDate) VALUES(?MessageId, ?DestServer, ?NextServer, ?DestApplication, ?MessageData, ?MessageDataLength, now())", + new MySqlParameter("?MessageId", messageRecord.MessageId), + new MySqlParameter("?DestServer", messageRecord.DestServer), + new MySqlParameter("?NextServer", messageRecord.NextServer), + new MySqlParameter("?DestApplication", messageRecord.DestApplication), + new MySqlParameter("?MessageData", bytesOfMessage), + new MySqlParameter("?MessageDataLength", bytesOfMessage.Length) + ); + messageRecord.Id = id; + return id; + + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = ?NextServer AND DestApplication = ?DestApplication AND Id >= ?Id ORDER BY Id ASC Limit ?LimitValue", + new MySqlParameter("?NextServer", nextServer), + new MySqlParameter("?DestApplication", destApplication), + new MySqlParameter("?Id", minId), + new MySqlParameter("?LimitValue", maxCount) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime)recordRow["RecordDate"] + } + ); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = ?NextServer AND DestApplication = ?DestApplication ORDER BY Id DESC Limit 1", + new MySqlParameter("?NextServer", nextServer), + new MySqlParameter("?DestApplication", destApplication) + ); + } + + /// + /// Gets waiting messages for an MDS server. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = ?NextServer AND Id >= ?Id ORDER BY Id ASC Limit ?LimitValue", + new MySqlParameter("?NextServer", nextServer), + new MySqlParameter("?Id", minId), + new MySqlParameter("?LimitValue", maxCount) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime)recordRow["RecordDate"] + } + ); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = ?NextServer ORDER BY Id DESC Limit 1", + new MySqlParameter("?NextServer", nextServer) + ); + } + + /// + /// Removes a message. + /// + /// id of message to remove + /// Effected rows count + public int RemoveMessage(int id) + { + return ExecuteNonQuery( + "DELETE FROM messages WHERE Id = ?Id", + new MySqlParameter("?Id", id) + ); + } + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + public void UpdateNextServer(string destServer, string nextServer) + { + ExecuteNonQuery( + "UPDATE messages SET NextServer = ?NextServer WHERE DestServer = ?DestServer", + new MySqlParameter("?NextServer", nextServer), + new MySqlParameter("?DestServer", destServer) + ); + } + + #endregion + + #region Helper Methods + + /// + /// Executes a query and returns effected rows count. + /// + /// Query to execute + /// Parameters + /// Effected rows count + private int ExecuteNonQuery(string query, params MySqlParameter[] parameters) + { + using (var connection = new MySqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new MySqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + return command.ExecuteNonQuery(); + } + } + } + + /// + /// This method is used to run an insert query and get inserted row's auto increment column's value. + /// + /// Insert query to be executed + /// Parameters + /// Auto increment column's value of inserted row + private int InsertAndGetLastId(string query, params MySqlParameter[] parameters) + { + using (var connection = new MySqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new MySqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + command.ExecuteNonQuery(); + } + + const string queryForLastId = "SELECT LAST_INSERT_ID() AS LastId;"; + using (var command = new MySqlCommand(queryForLastId, connection)) + { + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader["LastId"]); + } + + throw new MDSException("Can not be obtained last inserted id for query: " + query); + } + } + } + } + + /// + /// Runs a query and returns a DataTable. + /// + /// Query to execute + /// Parameters + /// Selected table + private DataTable GetTable(string query, params MySqlParameter[] parameters) + { + var table = new DataTable(); + using (var connection = new MySqlConnection(ConnectionString)) + { + using (var command = new MySqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var adapter = new MySqlDataAdapter(command)) + { + adapter.Fill(table); + return table; + } + } + } + } + + /// + /// Gets a record from a table. + /// + /// Select query + /// Select parameters + /// Returns found row as TableRecord object. If there is no row returns null + public TableRecord GetTableRecord(string query, params MySqlParameter[] parameters) + { + using (var connection = new MySqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new MySqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + var record = new TableRecord(); + for (var i = 0; i < reader.FieldCount; i++) + { + record[reader.GetName(i)] = reader[i]; + } + + return record; + } + + return null; + } + } + } + } + + /// + /// Executes a query and gets a Integer result. + /// If query returns no data, method returns 0. + /// + /// Query to execute + /// Parameters + /// Query result or 0 + public int GetScalarField(string query, params MySqlParameter[] parameters) + { + using (var connection = new MySqlConnection(ConnectionString)) + { + connection.Open(); + using (var command = new MySqlCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader[0]); + } + + return 0; + } + } + } + } + + #endregion + } +} diff --git a/src/MDSCore/Storage/MySqlStorage/MySqlODBCStorageManager.cs b/src/MDSCore/Storage/MySqlStorage/MySqlODBCStorageManager.cs new file mode 100644 index 0000000..437283f --- /dev/null +++ b/src/MDSCore/Storage/MySqlStorage/MySqlODBCStorageManager.cs @@ -0,0 +1,383 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Odbc; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Serialization; + +namespace MDS.Storage.MySqlStorage +{ + /// + /// This class is used to perform database operations on MySQL database engine using ODBC. + /// + public class MySqlOdbcStorageManager : IStorageManager + { + #region Private fields + + /// + /// Connection string to connect database. + /// + public string ConnectionString + { + get { return _connectionString; } + set { _connectionString = value; } + } + private string _connectionString = "uid=root;server=localhost;driver={MySQL ODBC 3.51 Driver};database=mds;STMT=set names latin5"; + + #endregion + + #region Public methods + + #region Unimplemented methods + + public void Start() + { + //No action + } + + public void Stop(bool waitToStop) + { + //No action + } + + public void WaitToStop() + { + //No action + } + + #endregion + + /// + /// Saves a MDSMessageRecord. + /// + /// MDSMessageRecord object to save + /// Auto Increment Id of saved message + public int StoreMessage(MDSMessageRecord messageRecord) + { + var bytesOfMessage = MDSSerializationHelper.SerializeToByteArray(messageRecord.Message); + var id = InsertAndGetLastId( + "INSERT INTO messages(MessageId, DestServer, NextServer, DestApplication, MessageData, MessageDataLength, RecordDate) VALUES(?,?,?,?,?,?,now());", + new OdbcParameter("?MessageId", messageRecord.MessageId), + new OdbcParameter("?DestServer", messageRecord.DestServer), + new OdbcParameter("?NextServer", messageRecord.NextServer), + new OdbcParameter("?DestApplication", messageRecord.DestApplication), + new OdbcParameter("?MessageData", bytesOfMessage), + new OdbcParameter("?MessageDataLength", bytesOfMessage.Length) + ); + messageRecord.Id = id; + return id; + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = ? AND DestApplication = ? AND Id >= ? ORDER BY Id ASC Limit ?;", + new OdbcParameter("?NextServer", nextServer), + new OdbcParameter("?DestApplication", destApplication), + new OdbcParameter("?Id", minId), + new OdbcParameter("?LimitValue", maxCount) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime) recordRow["RecordDate"] + } + ); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = ? AND DestApplication = ? ORDER BY Id DESC Limit 1;", + new OdbcParameter("?NextServer", nextServer), + new OdbcParameter("?DestApplication", destApplication) + ); + } + + /// + /// Gets waiting messages for an MDS server. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + var recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = ? AND Id >= ? ORDER BY Id ASC Limit ?;", + new OdbcParameter("?NextServer", nextServer), + new OdbcParameter("?Id", minId), + new OdbcParameter("?LimitValue", maxCount) + ); + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime) recordRow["RecordDate"] + } + ); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = ? ORDER BY Id DESC Limit 1;", + new OdbcParameter("?NextServer", nextServer) + ); + } + + /// + /// Removes a message. + /// + /// id of message to remove + /// Effected rows count + public int RemoveMessage(int id) + { + return ExecuteNonQuery( + "DELETE FROM messages WHERE Id = ?;", + new OdbcParameter("?Id", id) + ); + } + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + public void UpdateNextServer(string destServer, string nextServer) + { + ExecuteNonQuery( + "UPDATE messages SET NextServer = ? WHERE DestServer = ?;", + new OdbcParameter("?NextServer", nextServer), + new OdbcParameter("?DestServer", destServer) + ); + } + + #endregion + + #region Helper Methods + + /// + /// Executes a query and returns effected rows count. + /// + /// Query to execute + /// Parameters + /// Effected rows count + private int ExecuteNonQuery(string query, params OdbcParameter[] parameters) + { + using (var connection = new OdbcConnection(ConnectionString)) + { + connection.Open(); + using (var command = new OdbcCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + return command.ExecuteNonQuery(); + } + } + } + + /// + /// This method is used to run an insert query and get inserted row's auto increment column's value. + /// + /// Insert query to be executed + /// Parameters + /// Auto increment column's value of inserted row + private int InsertAndGetLastId(string query, params OdbcParameter[] parameters) + { + using (var connection = new OdbcConnection(ConnectionString)) + { + connection.Open(); + using (var command = new OdbcCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + command.ExecuteNonQuery(); + } + + const string queryForLastId = "SELECT LAST_INSERT_ID() AS LastId;"; + using (var command = new OdbcCommand(queryForLastId, connection)) + { + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader["LastId"]); + } + + throw new MDSException("Can not be obtained last inserted id for query: " + query); + } + } + } + } + + /// + /// Runs a query and returns a DataTable. + /// + /// Query to execute + /// Parameters + /// Selected table + private DataTable GetTable(string query, params OdbcParameter[] parameters) + { + var table = new DataTable(); + using (var connection = new OdbcConnection(ConnectionString)) + { + using (var command = new OdbcCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var adapter = new OdbcDataAdapter(command)) + { + adapter.Fill(table); + return table; + } + } + } + } + + /// + /// Gets a record from a table. + /// + /// Select query + /// Select parameters + /// Returns found row as TableRecord object. If there is no row returns null + public TableRecord GetTableRecord(string query, params OdbcParameter[] parameters) + { + using (var connection = new OdbcConnection(ConnectionString)) + { + connection.Open(); + using (var command = new OdbcCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + var record = new TableRecord(); + for (var i = 0; i < reader.FieldCount; i++) + { + record[reader.GetName(i)] = reader[i]; + } + + return record; + } + + return null; + } + } + } + } + + /// + /// Executes a query and gets a Integer result. + /// If query returns no data, method returns 0. + /// + /// Query to execute + /// Parameters + /// Query result or 0 + public int GetScalarField(string query, params OdbcParameter[] parameters) + { + using (var connection = new OdbcConnection(ConnectionString)) + { + connection.Open(); + using (var command = new OdbcCommand(query, connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader[0]); + } + + return 0; + } + } + } + } + + #endregion + } +} diff --git a/src/MDSCore/Storage/SQLiteStorage/SQLiteStorageManager.cs b/src/MDSCore/Storage/SQLiteStorage/SQLiteStorageManager.cs new file mode 100644 index 0000000..266017e --- /dev/null +++ b/src/MDSCore/Storage/SQLiteStorage/SQLiteStorageManager.cs @@ -0,0 +1,426 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Data; +using System.IO; +using System.Data.SQLite; +using MDS.Communication.Messages; +using MDS.Exceptions; +using MDS.Serialization; + +namespace MDS.Storage.SQLiteStorage +{ + /// + /// This class is used to perform database operations on SQLite database engine. + /// To use this storage manager, SqliteDB\MDS.s3db file must be in root folder of MDS server. + /// + public class SqliteStorageManager : IStorageManager + { + #region Private fields + + /// + /// Connection string to connect database. + /// + private string ConnectionString { get; set; } + + /// + /// Open database connection. + /// + private SQLiteConnection _connection; + + /// + /// This object is used to synchronized access to database. One thread in a time. + /// + private readonly object _syncObj = new object(); + + #endregion + + #region Public methods + + /// + /// Creates a new SqliteStorageManager object. + /// + public SqliteStorageManager() + { + var csb = new SQLiteConnectionStringBuilder + { + DataSource = Path.Combine(GeneralHelper.GetCurrentDirectory(), @"SqliteDB\MDS.s3db"), + Version = 3, + Pooling = true, + ReadOnly = false, + SyncMode = SynchronizationModes.Off + }; + ConnectionString = csb.ConnectionString; + } + + /// + /// Initializes SQLite connection. + /// + public void Start() + { + _connection = new SQLiteConnection(ConnectionString); + _connection.Open(); + } + + public void Stop(bool waitToStop) + { + _connection.Dispose(); + } + + public void WaitToStop() + { + //No action + } + + /// + /// Saves a MDSMessageRecord. + /// + /// MDSMessageRecord object to save + /// Auto Increment Id of saved message + public int StoreMessage(MDSMessageRecord messageRecord) + { + lock (_syncObj) + { + var bytesOfMessage = MDSSerializationHelper.SerializeToByteArray(messageRecord.Message); + var id = InsertAndGetLastId( + "INSERT INTO messages(MessageId, DestServer, NextServer, DestApplication, MessageData, MessageDataLength, RecordDate) VALUES(@MessageId,@DestServer,@NextServer,@DestApplication,@MessageData,@MessageDataLength,@RecordDate);", + new SQLiteParameter("@MessageId", messageRecord.MessageId), + new SQLiteParameter("@DestServer", messageRecord.DestServer), + new SQLiteParameter("@NextServer", messageRecord.NextServer), + new SQLiteParameter("@DestApplication", messageRecord.DestApplication), + new SQLiteParameter("@MessageData", bytesOfMessage), + new SQLiteParameter("@MessageDataLength", bytesOfMessage.Length), + new SQLiteParameter("@RecordDate", DateTime.Now) + ); + messageRecord.Id = id; + return id; + } + } + + /// + /// Gets waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfApplication(string nextServer, string destApplication, int minId, int maxCount) + { + DataTable recordsTable; + lock (_syncObj) + { + recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = @NextServer AND DestApplication = @DestApplication AND Id >= @Id ORDER BY Id ASC Limit @LimitValue", + new SQLiteParameter("@NextServer", nextServer), + new SQLiteParameter("@DestApplication", destApplication), + new SQLiteParameter("@Id", minId), + new SQLiteParameter("@LimitValue", maxCount) + ); + } + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime)recordRow["RecordDate"] + } + ); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an application. + /// + /// Next server name + /// Destination application name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfApplication(string nextServer, string destApplication) + { + lock (_syncObj) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = @NextServer AND DestApplication = @DestApplication ORDER BY Id DESC Limit 1", + new SQLiteParameter("@NextServer", nextServer), + new SQLiteParameter("@DestApplication", destApplication) + ); + } + } + + /// + /// Gets waiting messages for an MDS server. + /// + /// Next server name + /// Minimum Id (as start Id) + /// Max record count to get + /// Records gotten from database. + public List GetWaitingMessagesOfServer(string nextServer, int minId, int maxCount) + { + DataTable recordsTable; + lock (_syncObj) + { + recordsTable = GetTable( + "SELECT * FROM messages WHERE NextServer = @NextServer AND Id >= @Id ORDER BY Id ASC Limit @LimitValue", + new SQLiteParameter("@NextServer", nextServer), + new SQLiteParameter("@Id", minId), + new SQLiteParameter("@LimitValue", maxCount) + ); + } + + var recordsList = new List(recordsTable.Rows.Count); + foreach (DataRow recordRow in recordsTable.Rows) + { + recordsList.Add( + new MDSMessageRecord + { + Id = Convert.ToInt32(recordRow["Id"]), + DestApplication = recordRow["DestApplication"] as string, + DestServer = recordRow["DestServer"] as string, + NextServer = recordRow["NextServer"] as string, + Message = MDSSerializationHelper.DeserializeFromByteArray(() => new MDSDataTransferMessage(), (byte[])recordRow["MessageData"]), + MessageId = recordRow["MessageId"] as string, + RecordDate = (DateTime) recordRow["RecordDate"] + }); + } + + return recordsList; + } + + /// + /// Gets last (biggest) Id of waiting messages for an MDS server. + /// + /// Next server name + /// last (biggest) Id of waiting messages + public int GetMaxWaitingMessageIdOfServer(string nextServer) + { + lock (_syncObj) + { + return GetScalarField( + "SELECT Id FROM messages WHERE NextServer = @NextServer ORDER BY Id DESC Limit 1", + new SQLiteParameter("@NextServer", nextServer) + ); + } + } + + /// + /// Removes a message. + /// + /// id of message to remove + /// Effected rows count + public int RemoveMessage(int id) + { + lock (_syncObj) + { + return ExecuteNonQuery( + "DELETE FROM messages WHERE Id = @Id", + new SQLiteParameter("@Id", id) + ); + } + } + + /// + /// This method is used to set Next Server for a Destination Server. + /// It is used to update database records when Server Graph changed. + /// + /// Destination server of messages + /// Next server of messages for destServer + public void UpdateNextServer(string destServer, string nextServer) + { + ExecuteNonQuery( + "UPDATE messages SET NextServer = @NextServer WHERE DestServer = @DestServer", + new SQLiteParameter("@NextServer", nextServer), + new SQLiteParameter("@DestServer", destServer) + ); + } + + #endregion + + #region Helper Methods + + /// + /// Executes a query and returns effected rows count. + /// + /// Query to execute + /// Parameters + /// Effected rows count + private int ExecuteNonQuery(string query, params SQLiteParameter[] parameters) + { + int result; + using (var transaction = _connection.BeginTransaction()) + { + using (var command = new SQLiteCommand(query, _connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + result = command.ExecuteNonQuery(); + } + + transaction.Commit(); + } + + return result; + } + + /// + /// This method is used to run an insert query and get inserted row's auto increment column's value. + /// + /// Insert query to be executed + /// Parameters + /// Auto increment column's value of inserted row + private int InsertAndGetLastId(string query, params SQLiteParameter[] parameters) + { + var insertedId = 0; + using (var transaction = _connection.BeginTransaction()) + { + using (var command = new SQLiteCommand(query, _connection, transaction)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + command.ExecuteNonQuery(); + } + + const string queryForLastId = "SELECT last_insert_rowid() AS LastId;"; + using (var command = new SQLiteCommand(queryForLastId, _connection, transaction)) + { + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + insertedId = Convert.ToInt32(reader["LastId"]); + } + } + } + + transaction.Commit(); + } + + if(insertedId <= 0) + { + throw new MDSException("Can not be obtained last inserted id for query: " + query); + } + + return insertedId; + } + + /// + /// Runs a query and returns a DataTable. + /// + /// Query to execute + /// Parameters + /// Selected table + private DataTable GetTable(string query, params SQLiteParameter[] parameters) + { + var table = new DataTable(); + using (var command = new SQLiteCommand(query, _connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var adapter = new SQLiteDataAdapter(command)) + { + adapter.Fill(table); + return table; + } + } + } + + /// + /// Gets a record from a table. + /// + /// Select query + /// Select parameters + /// Returns found row as TableRecord object. If there is no row returns null + public TableRecord GetTableRecord(string query, params SQLiteParameter[] parameters) + { + using (var command = new SQLiteCommand(query, _connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + var record = new TableRecord(); + for (var i = 0; i < reader.FieldCount; i++) + { + record[reader.GetName(i)] = reader[i]; + } + + return record; + } + + return null; + } + } + } + + /// + /// Executes a query and gets a Integer result. + /// If query returns no data, method returns 0. + /// + /// Query to execute + /// Parameters + /// Query result or 0 + public int GetScalarField(string query, params SQLiteParameter[] parameters) + { + using (var command = new SQLiteCommand(query, _connection)) + { + foreach (var parameter in parameters) + { + command.Parameters.Add(parameter); + } + + using (var reader = command.ExecuteReader()) + { + if (reader.Read()) + { + return Convert.ToInt32(reader[0]); + } + + return 0; + } + } + } + + #endregion + } +} diff --git a/src/MDSCore/Storage/StorageManagerFactory.cs b/src/MDSCore/Storage/StorageManagerFactory.cs new file mode 100644 index 0000000..1e346f2 --- /dev/null +++ b/src/MDSCore/Storage/StorageManagerFactory.cs @@ -0,0 +1,49 @@ +using System; +using MDS.Storage.MemoryStorage; +using MDS.Storage.MsSqlStorage; +using MDS.Storage.MySqlStorage; +using MDS.Storage.SQLiteStorage; +using MDS.Settings; + +namespace MDS.Storage +{ + public static class StorageManagerFactory + { + /// + /// Creates Storage Manager according to StorageType value in Settings file. + /// + /// Storame Manager + public static IStorageManager CreateStorageManager() + { + //Get a reference to the settings + var settings = MDSSettings.Instance; + + //Create storage manager according to the settings + var storageType = settings["StorageType"]; + IStorageManager storageManager; + if (storageType.Equals("MySQL-ODBC", StringComparison.OrdinalIgnoreCase)) + { + storageManager = new MySqlOdbcStorageManager { ConnectionString = settings["ConnectionString"] }; + } + else if (storageType.Equals("MySQL-Net", StringComparison.OrdinalIgnoreCase)) + { + storageManager = new MySqlNetStorageManager { ConnectionString = settings["ConnectionString"] }; + } + else if (storageType.Equals("MSSQL", StringComparison.OrdinalIgnoreCase)) + { + storageManager = new MsSqlStorageManager { ConnectionString = settings["ConnectionString"] }; + } + else if (storageType.Equals("SQLite", StringComparison.OrdinalIgnoreCase)) + { + storageManager = new SqliteStorageManager(); + } + else //Default storage manager + { + storageManager = new MemoryStorageManager(); + } + + //Wrap storageManager with FaultToleratedStorageManagerWrapper and return it + return new FaultToleratedStorageManagerWrapper(storageManager); + } + } +} diff --git a/src/MDSCore/Storage/TableRecord.cs b/src/MDSCore/Storage/TableRecord.cs new file mode 100644 index 0000000..e4be183 --- /dev/null +++ b/src/MDSCore/Storage/TableRecord.cs @@ -0,0 +1,60 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System.Collections.Generic; + +namespace MDS.Storage +{ + /// + /// Represents a single row in a table in a database. + /// + public class TableRecord + { + /// + /// Gets/Sets a field value. + /// + /// Name of field + /// Value of field + public object this[string fieldName] + { + get + { + return _fields[fieldName]; + } + + set + { + _fields[fieldName] = value; + } + } + + /// + /// Fields and their values in this record. + /// + private readonly SortedList _fields; + + /// + /// Creates a new TableRecord object. + /// + public TableRecord() + { + _fields = new SortedList(); + } + } +} diff --git a/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.disco b/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.disco new file mode 100644 index 0000000..f535267 --- /dev/null +++ b/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.disco @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.wsdl b/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.wsdl new file mode 100644 index 0000000..d3d86d9 --- /dev/null +++ b/src/MDSCore/Web References/MDSAppWebServiceRef/MDSAppService.wsdl @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Receives incoming messages to this web service. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.cs b/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.cs new file mode 100644 index 0000000..584f350 --- /dev/null +++ b/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.cs @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4952 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// +// This source code was auto-generated by Microsoft.VSDesigner, Version 2.0.50727.4952. +// +#pragma warning disable 1591 + +namespace MDS.MDSAppWebServiceRef { + using System.Diagnostics; + using System.Web.Services; + using System.ComponentModel; + using System.Web.Services.Protocols; + using System; + using System.Xml.Serialization; + + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.4927")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Web.Services.WebServiceBindingAttribute(Name="MDSAppServiceSoap", Namespace="http://www.dotnetmq.com/mds")] + public partial class MDSAppService : System.Web.Services.Protocols.SoapHttpClientProtocol { + + private System.Threading.SendOrPostCallback ReceiveMDSMessageOperationCompleted; + + private bool useDefaultCredentialsSetExplicitly; + + /// + public MDSAppService() { + this.Url = global::MDS.Properties.Settings.Default.MDSCore_MDSAppWebServiceRef_MDSAppService; + if ((this.IsLocalFileSystemWebService(this.Url) == true)) { + this.UseDefaultCredentials = true; + this.useDefaultCredentialsSetExplicitly = false; + } + else { + this.useDefaultCredentialsSetExplicitly = true; + } + } + + public new string Url { + get { + return base.Url; + } + set { + if ((((this.IsLocalFileSystemWebService(base.Url) == true) + && (this.useDefaultCredentialsSetExplicitly == false)) + && (this.IsLocalFileSystemWebService(value) == false))) { + base.UseDefaultCredentials = false; + } + base.Url = value; + } + } + + public new bool UseDefaultCredentials { + get { + return base.UseDefaultCredentials; + } + set { + base.UseDefaultCredentials = value; + this.useDefaultCredentialsSetExplicitly = true; + } + } + + /// + public event ReceiveMDSMessageCompletedEventHandler ReceiveMDSMessageCompleted; + + /// + [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://www.dotnetmq.com/mds/ReceiveMDSMessage", RequestNamespace="http://www.dotnetmq.com/mds", ResponseNamespace="http://www.dotnetmq.com/mds", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] + [return: System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] + public byte[] ReceiveMDSMessage([System.Xml.Serialization.XmlElementAttribute(DataType="base64Binary")] byte[] bytesOfMessage) { + object[] results = this.Invoke("ReceiveMDSMessage", new object[] { + bytesOfMessage}); + return ((byte[])(results[0])); + } + + /// + public void ReceiveMDSMessageAsync(byte[] bytesOfMessage) { + this.ReceiveMDSMessageAsync(bytesOfMessage, null); + } + + /// + public void ReceiveMDSMessageAsync(byte[] bytesOfMessage, object userState) { + if ((this.ReceiveMDSMessageOperationCompleted == null)) { + this.ReceiveMDSMessageOperationCompleted = new System.Threading.SendOrPostCallback(this.OnReceiveMDSMessageOperationCompleted); + } + this.InvokeAsync("ReceiveMDSMessage", new object[] { + bytesOfMessage}, this.ReceiveMDSMessageOperationCompleted, userState); + } + + private void OnReceiveMDSMessageOperationCompleted(object arg) { + if ((this.ReceiveMDSMessageCompleted != null)) { + System.Web.Services.Protocols.InvokeCompletedEventArgs invokeArgs = ((System.Web.Services.Protocols.InvokeCompletedEventArgs)(arg)); + this.ReceiveMDSMessageCompleted(this, new ReceiveMDSMessageCompletedEventArgs(invokeArgs.Results, invokeArgs.Error, invokeArgs.Cancelled, invokeArgs.UserState)); + } + } + + /// + public new void CancelAsync(object userState) { + base.CancelAsync(userState); + } + + private bool IsLocalFileSystemWebService(string url) { + if (((url == null) + || (url == string.Empty))) { + return false; + } + System.Uri wsUri = new System.Uri(url); + if (((wsUri.Port >= 1024) + && (string.Compare(wsUri.Host, "localHost", System.StringComparison.OrdinalIgnoreCase) == 0))) { + return true; + } + return false; + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.4927")] + public delegate void ReceiveMDSMessageCompletedEventHandler(object sender, ReceiveMDSMessageCompletedEventArgs e); + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Web.Services", "2.0.50727.4927")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class ReceiveMDSMessageCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs { + + private object[] results; + + internal ReceiveMDSMessageCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) : + base(exception, cancelled, userState) { + this.results = results; + } + + /// + public byte[] Result { + get { + this.RaiseExceptionIfNecessary(); + return ((byte[])(this.results[0])); + } + } + } +} + +#pragma warning restore 1591 \ No newline at end of file diff --git a/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.map b/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.map new file mode 100644 index 0000000..7d4ddd8 --- /dev/null +++ b/src/MDSCore/Web References/MDSAppWebServiceRef/Reference.map @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/MDSCore/app.config b/src/MDSCore/app.config new file mode 100644 index 0000000..4ae9200 --- /dev/null +++ b/src/MDSCore/app.config @@ -0,0 +1,19 @@ + + + + +
+ + + + + + + + + + http://localhost:2111/MDSWebServiceInterface/MDSAppService.asmx + + + + \ No newline at end of file diff --git a/src/MDSManager/App.config b/src/MDSManager/App.config new file mode 100644 index 0000000..ff3cef3 --- /dev/null +++ b/src/MDSManager/App.config @@ -0,0 +1,36 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MDSManager/Changes.txt b/src/MDSManager/Changes.txt new file mode 100644 index 0000000..0474ec2 --- /dev/null +++ b/src/MDSManager/Changes.txt @@ -0,0 +1,2 @@ +# Version 0.9.0.0 - 09.05.2011 - Halil İbrahim KALKAN + First release. \ No newline at end of file diff --git a/src/MDSManager/Liscense.txt b/src/MDSManager/Liscense.txt new file mode 100644 index 0000000..83a488d --- /dev/null +++ b/src/MDSManager/Liscense.txt @@ -0,0 +1,16 @@ +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/src/MDSManager/MDSManager.csproj b/src/MDSManager/MDSManager.csproj new file mode 100644 index 0000000..a1ecd2e --- /dev/null +++ b/src/MDSManager/MDSManager.csproj @@ -0,0 +1,180 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {FD323317-5B22-413C-8ADC-6701C2B7EB76} + WinExe + Properties + MDS + MDSManager + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\Release\ + TRACE + prompt + 4 + + + + False + ..\Dependencies\Libraries\log4net.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + Form + + + AddNewApplicationForm.cs + + + Form + + + ApplicationListForm.cs + + + Form + + + EditApplicationWebServicesForm.cs + + + Form + + + ConnectToServerForm.cs + + + Form + + + MainForm.cs + + + Form + + + EditRouteForm.cs + + + Form + + + EditServerForm.cs + + + Form + + + RoutesForm.cs + + + Component + + + Form + + + ServerGraphForm.cs + + + + + AddNewApplicationForm.cs + + + ApplicationListForm.cs + + + EditApplicationWebServicesForm.cs + + + ConnectToServerForm.cs + + + MainForm.cs + + + EditRouteForm.cs + + + EditServerForm.cs + + + RoutesForm.cs + + + ServerGraphForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + {055149D1-A267-4E2E-B8AE-EA7848A45701} + MDSCommonLib + + + + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.Designer.cs b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.Designer.cs new file mode 100644 index 0000000..30c1ba7 --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.Designer.cs @@ -0,0 +1,129 @@ +namespace MDS.Management.GUI.ClientApplications +{ + partial class AddNewApplicationForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainPanel = new System.Windows.Forms.Panel(); + this.btnAdd = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.mainPanel.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnAdd); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(299, 100); + this.mainPanel.TabIndex = 2; + // + // btnAdd + // + this.btnAdd.BackColor = System.Drawing.Color.ForestGreen; + this.btnAdd.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnAdd.ForeColor = System.Drawing.Color.White; + this.btnAdd.Location = new System.Drawing.Point(135, 62); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(155, 28); + this.btnAdd.TabIndex = 7; + this.btnAdd.Text = "Add Application"; + this.btnAdd.UseVisualStyleBackColor = false; + this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.lblApplicationListHeader); + this.groupBox1.Controls.Add(this.txtName); + this.groupBox1.Location = new System.Drawing.Point(8, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(282, 50); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Application Properties"; + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.LightSlateGray; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(6, 19); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(120, 23); + this.lblApplicationListHeader.TabIndex = 2; + this.lblApplicationListHeader.Text = "Name:"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtName + // + this.txtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtName.Location = new System.Drawing.Point(127, 19); + this.txtName.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtName.MaxLength = 50; + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(149, 23); + this.txtName.TabIndex = 4; + // + // AddNewApplicationForm + // + this.AcceptButton = this.btnAdd; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(299, 100); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AddNewApplicationForm"; + this.Text = "Add New Application"; + this.Load += new System.EventHandler(this.AddNewApplicationForm_Load); + this.mainPanel.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.Button btnAdd; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.TextBox txtName; + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.cs b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.cs new file mode 100644 index 0000000..0294fee --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.cs @@ -0,0 +1,62 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Windows.Forms; +using MDS.GUI; + +namespace MDS.Management.GUI.ClientApplications +{ + public partial class AddNewApplicationForm : Form + { + public string ApplicationName { get; set; } + + public AddNewApplicationForm() + { + InitializeComponent(); + } + + private void btnAdd_Click(object sender, EventArgs e) + { + var appName = txtName.Text.Trim(); + if (string.IsNullOrEmpty(appName)) + { + MDSGuiHelper.ShowWarningMessage("Application name can not be empty!"); + return; + } + + ApplicationName = appName; + Close(); + } + + private void AddNewApplicationForm_Load(object sender, EventArgs e) + { + PrepareForm(); + } + + /// + /// Prepares form. + /// + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + } + } +} diff --git a/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.resx b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/AddNewApplicationForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.Designer.cs b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.Designer.cs new file mode 100644 index 0000000..7c60c1a --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.Designer.cs @@ -0,0 +1,196 @@ +namespace MDS.Management.GUI.ClientApplications +{ + partial class ApplicationListForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.mainPanel = new System.Windows.Forms.Panel(); + this.btnRefreshList = new System.Windows.Forms.Button(); + this.btnRemoveApplication = new System.Windows.Forms.Button(); + this.btnAddApplication = new System.Windows.Forms.Button(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.gwApplicationList = new System.Windows.Forms.DataGridView(); + this.cApplicationName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.cConnectedClients = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ApplicationMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.editWebServicesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mainPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gwApplicationList)).BeginInit(); + this.ApplicationMenu.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnRefreshList); + this.mainPanel.Controls.Add(this.btnRemoveApplication); + this.mainPanel.Controls.Add(this.btnAddApplication); + this.mainPanel.Controls.Add(this.lblApplicationListHeader); + this.mainPanel.Controls.Add(this.gwApplicationList); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(557, 362); + this.mainPanel.TabIndex = 0; + // + // btnRefreshList + // + this.btnRefreshList.BackColor = System.Drawing.Color.DodgerBlue; + this.btnRefreshList.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnRefreshList.ForeColor = System.Drawing.Color.White; + this.btnRefreshList.Location = new System.Drawing.Point(3, 329); + this.btnRefreshList.Name = "btnRefreshList"; + this.btnRefreshList.Size = new System.Drawing.Size(137, 28); + this.btnRefreshList.TabIndex = 4; + this.btnRefreshList.Text = "Refresh List"; + this.btnRefreshList.UseVisualStyleBackColor = false; + this.btnRefreshList.Click += new System.EventHandler(this.btnRefreshList_Click); + // + // btnRemoveApplication + // + this.btnRemoveApplication.BackColor = System.Drawing.Color.Crimson; + this.btnRemoveApplication.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnRemoveApplication.ForeColor = System.Drawing.Color.White; + this.btnRemoveApplication.Location = new System.Drawing.Point(146, 329); + this.btnRemoveApplication.Name = "btnRemoveApplication"; + this.btnRemoveApplication.Size = new System.Drawing.Size(200, 28); + this.btnRemoveApplication.TabIndex = 3; + this.btnRemoveApplication.Text = "Remove Application"; + this.btnRemoveApplication.UseVisualStyleBackColor = false; + this.btnRemoveApplication.Click += new System.EventHandler(this.btnRemoveApplication_Click); + // + // btnAddApplication + // + this.btnAddApplication.BackColor = System.Drawing.Color.ForestGreen; + this.btnAddApplication.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnAddApplication.ForeColor = System.Drawing.Color.White; + this.btnAddApplication.Location = new System.Drawing.Point(352, 329); + this.btnAddApplication.Name = "btnAddApplication"; + this.btnAddApplication.Size = new System.Drawing.Size(200, 28); + this.btnAddApplication.TabIndex = 2; + this.btnAddApplication.Text = "Add new Application"; + this.btnAddApplication.UseVisualStyleBackColor = false; + this.btnAddApplication.Click += new System.EventHandler(this.btnAddApplication_Click); + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.DodgerBlue; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(3, 3); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(550, 23); + this.lblApplicationListHeader.TabIndex = 1; + this.lblApplicationListHeader.Text = "All Applications"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // gwApplicationList + // + this.gwApplicationList.AllowUserToAddRows = false; + this.gwApplicationList.AllowUserToDeleteRows = false; + this.gwApplicationList.AllowUserToResizeRows = false; + this.gwApplicationList.BackgroundColor = System.Drawing.Color.AliceBlue; + this.gwApplicationList.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gwApplicationList.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.cApplicationName, + this.cConnectedClients}); + this.gwApplicationList.ContextMenuStrip = this.ApplicationMenu; + this.gwApplicationList.Location = new System.Drawing.Point(3, 25); + this.gwApplicationList.MultiSelect = false; + this.gwApplicationList.Name = "gwApplicationList"; + this.gwApplicationList.ReadOnly = true; + this.gwApplicationList.RowHeadersVisible = false; + this.gwApplicationList.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.gwApplicationList.Size = new System.Drawing.Size(550, 300); + this.gwApplicationList.TabIndex = 0; + // + // cApplicationName + // + this.cApplicationName.DataPropertyName = "ApplicationName"; + this.cApplicationName.HeaderText = "Application Name"; + this.cApplicationName.Name = "cApplicationName"; + this.cApplicationName.ReadOnly = true; + this.cApplicationName.Width = 350; + // + // cConnectedClients + // + this.cConnectedClients.DataPropertyName = "ConnectedClients"; + this.cConnectedClients.HeaderText = "Connected Clients"; + this.cConnectedClients.Name = "cConnectedClients"; + this.cConnectedClients.ReadOnly = true; + this.cConnectedClients.Width = 180; + // + // ApplicationMenu + // + this.ApplicationMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.editWebServicesToolStripMenuItem}); + this.ApplicationMenu.Name = "ApplicationMenu"; + this.ApplicationMenu.Size = new System.Drawing.Size(172, 26); + // + // editWebServicesToolStripMenuItem + // + this.editWebServicesToolStripMenuItem.Name = "editWebServicesToolStripMenuItem"; + this.editWebServicesToolStripMenuItem.Size = new System.Drawing.Size(171, 22); + this.editWebServicesToolStripMenuItem.Text = "Edit Web Services"; + this.editWebServicesToolStripMenuItem.Click += new System.EventHandler(this.editWebServicesToolStripMenuItem_Click); + // + // ApplicationListForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(557, 362); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "ApplicationListForm"; + this.Text = "Client Application List"; + this.Load += new System.EventHandler(this.ApplicationList_Load); + this.mainPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gwApplicationList)).EndInit(); + this.ApplicationMenu.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.DataGridView gwApplicationList; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.Button btnAddApplication; + private System.Windows.Forms.DataGridViewTextBoxColumn cApplicationName; + private System.Windows.Forms.DataGridViewTextBoxColumn cConnectedClients; + private System.Windows.Forms.Button btnRemoveApplication; + private System.Windows.Forms.Button btnRefreshList; + private System.Windows.Forms.ContextMenuStrip ApplicationMenu; + private System.Windows.Forms.ToolStripMenuItem editWebServicesToolStripMenuItem; + + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.cs b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.cs new file mode 100644 index 0000000..5866cd7 --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.cs @@ -0,0 +1,421 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; +using log4net; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Exceptions; +using MDS.GUI; + +namespace MDS.Management.GUI.ClientApplications +{ + public partial class ApplicationListForm : Form + { + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to MDSController object. + /// All communication with MDS server is performed using this object. + /// + private readonly MDSController _controller; + + /// + /// This list is used as DataSource of data grid. + /// It stores client applications. + /// + private readonly List _applicationList; + + /// + /// This list is used to set data grid's datasource to clear items. + /// + private readonly List _emptyList; + + #endregion + + #region Constructor + + /// + /// Creates a new ApplicationListForm object. + /// + /// Reference to MDSController object + public ApplicationListForm(MDSController controller) + { + _controller = controller; + _applicationList = new List(); + _emptyList = new List(); + InitializeComponent(); + } + + #endregion + + #region Public methods + + /// + /// This method is called by MainForm when a ClientApplicationRefreshEventMessage received from MDS Server. + /// + /// Incoming message + public void GetClientApplicationRefreshEventMessage(ClientApplicationRefreshEventMessage message) + { + RefreshClientApplication(message); + } + + /// + /// This method is called by MainForm when a ClientApplicationRemovedEventMessage received from MDS Server. + /// + /// + public void GetClientApplicationRemovedEventMessage(ClientApplicationRemovedEventMessage message) + { + lock (_applicationList) + { + RemoveApplicationFromList(message.ApplicationName); + } + } + + #endregion + + #region Private methods + + #region Form events + + private void ApplicationList_Load(object sender, EventArgs e) + { + PrepareForm(); + GetApplicationsFromServer(); + } + + private void btnAddApplication_Click(object sender, EventArgs e) + { + var addApplicationForm = new AddNewApplicationForm(); + addApplicationForm.ShowDialog(); + if (string.IsNullOrEmpty(addApplicationForm.ApplicationName)) + { + return; + } + + try + { + _controller.SendMessage( + new AddNewApplicationMessage + { + ApplicationName = addApplicationForm.ApplicationName + }); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Application can not be added. Detail: " + ex.Message); + } + } + + private void btnRemoveApplication_Click(object sender, EventArgs e) + { + lock (_applicationList) + { + //Check if user selected any application to remove + if (gwApplicationList.SelectedRows.Count <= 0) + { + return; + } + + var selectedIndex = gwApplicationList.SelectedRows[0].Index; + if ((selectedIndex < 0) || (selectedIndex >= _applicationList.Count)) + { + return; + } + + //Get confirmation from user to remove application. + var applicationName = _applicationList[selectedIndex].ApplicationName; + var dialogResult = + MDSGuiHelper.ShowQuestionDialog("Are you sure to remove '" + applicationName + "' application from MDS", + "Attention! You are removing an application", + MessageBoxDefaultButton.Button2); + if (dialogResult != DialogResult.Yes) + { + return; + } + + try + { + //Send RemoveApplicationMessage to MDS server + var message = _controller.SendMessageAndGetResponse( + new RemoveApplicationMessage + { + ApplicationName = applicationName + }); + + //Check response message + if (message.MessageTypeId != ControlMessageFactory.MessageTypeIdRemoveApplicationResponseMessage) + { + throw new MDSException("Response message to RemoveApplicationMessage must be as RemoveApplicationResponseMessage"); + } + + var responseMessage = message as RemoveApplicationResponseMessage; + if (responseMessage == null) + { + throw new MDSException("Incorrect type. MessageTypeId = " + message.MessageTypeId + ", but Type of object: " + message.GetType().Name); + } + + //Evaluate response message + if (responseMessage.Removed) + { + RemoveApplicationFromList(responseMessage.ApplicationName); + } + else + { + MDSGuiHelper.ShowWarningMessage(responseMessage.ResultMessage, applicationName + " application can not be removed!"); + } + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage(applicationName + " application can not be removed. Detail: " + ex.Message); + } + } + } + + private void btnRefreshList_Click(object sender, EventArgs e) + { + GetApplicationsFromServer(); + } + + private void editWebServicesToolStripMenuItem_Click(object sender, EventArgs e) + { + string applicationName; + lock (_applicationList) + { + //Check if user selected any application to remove + if (gwApplicationList.SelectedRows.Count <= 0) + { + return; + } + + var selectedIndex = gwApplicationList.SelectedRows[0].Index; + if ((selectedIndex < 0) || (selectedIndex >= _applicationList.Count)) + { + return; + } + + //Get confirmation from user to remove application. + applicationName = _applicationList[selectedIndex].ApplicationName; + } + + var editWebServiceForm = new EditApplicationWebServicesForm(_controller, applicationName); + editWebServiceForm.ShowDialog(); + } + + #endregion + + #region Other private methods + + /// + /// Gets all applications from server and fills into list. + /// + private void GetApplicationsFromServer() + { + try + { + //Send a message to MDS server to get list of client applications, get response and fill data grid. + var message = _controller.SendMessageAndGetResponse(new GetApplicationListMessage()); + if (message.MessageTypeId != ControlMessageFactory.MessageTypeIdGetApplicationListResponseMessage) + { + throw new MDSException("Response message to GetApplicationListMessage must be a GetApplicationListResponseMessage"); + } + + var applicationListMessage = message as GetApplicationListResponseMessage; + if (applicationListMessage == null) + { + throw new MDSException("Incorrect message type. MessageTypeId = " + message.MessageTypeId + ", but Type of object: " + message.GetType().Name); + } + + FillApplicationList(applicationListMessage.ClientApplications); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Application list can not received from MDS Server. Detail: " + ex.Message); + } + } + + /// + /// Fills data grid (gwApplicationList) by list of applications. + /// + /// Application list + private void FillApplicationList(IEnumerable applicationInfos) + { + lock (_applicationList) + { + EmptyList(); + _applicationList.Clear(); + foreach (var applicationInfo in applicationInfos) + { + _applicationList.Add( + new ApplicationListItem + { + ApplicationName = applicationInfo.Name, + ConnectedClients = applicationInfo.CommunicatorCount + }); + } + + RefreshList(); + } + } + + /// + /// Processes a ClientApplicationRefreshEventMessage. + /// + /// Message to process + private void RefreshClientApplication(ClientApplicationRefreshEventMessage message) + { + lock (_applicationList) + { + try + { + EmptyList(); + + //Find application in list and change properties + foreach (var application in _applicationList) + { + if (message.Name.Equals(application.ApplicationName, StringComparison.OrdinalIgnoreCase)) + { + application.ConnectedClients = message.CommunicatorCount; + return; + } + } + + //Add to list if it is not in list + _applicationList.Add( + new ApplicationListItem + { + ApplicationName = message.Name, + ConnectedClients = message.CommunicatorCount + }); + } + finally + { + RefreshList(); + } + } + } + + /// + /// Removes application from shown application list. + /// + /// Name of the application + private void RemoveApplicationFromList(string name) + { + //Find application in list + ApplicationListItem application = null; + foreach (var applicationListItem in _applicationList) + { + if (name.Equals(applicationListItem.ApplicationName, StringComparison.OrdinalIgnoreCase)) + { + application = applicationListItem; + break; + } + } + + //No action if application is not in list. + if (application == null) + { + return; + } + + EmptyList(); + _applicationList.Remove(application); + RefreshList(); + } + + /// + /// Empties data grid. + /// + private void EmptyList() + { + if (InvokeRequired) + { + Invoke(new Action(EmptyList)); + } + else + { + gwApplicationList.DataSource = _emptyList; + } + } + + /// + /// Refreshes data grid. + /// + private void RefreshList() + { + if (InvokeRequired) + { + Invoke(new Action(RefreshList)); + } + else + { + gwApplicationList.DataSource = _emptyList; + gwApplicationList.DataSource = _applicationList; + } + } + + /// + /// Prepares form. This method is called while form is opening. + /// + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + } + + #endregion + + #endregion + + #region Sub classes + + /// + /// Represents an item in application list. + /// + private class ApplicationListItem + { + /// + /// Name of the application. + /// + public string ApplicationName { get; set; } + + /// + /// Currently connected communicator count. + /// + public int ConnectedClients { get; set; } + } + + #endregion + } +} diff --git a/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.resx b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.resx new file mode 100644 index 0000000..ea052ac --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/ApplicationListForm.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + 17, 17 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.Designer.cs b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.Designer.cs new file mode 100644 index 0000000..e1cdf01 --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.Designer.cs @@ -0,0 +1,255 @@ +namespace MDS.Management.GUI.ClientApplications +{ + partial class EditApplicationWebServicesForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.mainPanel = new System.Windows.Forms.Panel(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.gwWebServices = new System.Windows.Forms.DataGridView(); + this.cApplicationName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Application = new System.Windows.Forms.GroupBox(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.txtAppName = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnAdd = new System.Windows.Forms.Button(); + this.txtServiceUrl = new System.Windows.Forms.TextBox(); + this.WebServicesMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.removeWebServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.btnSaveChanges = new System.Windows.Forms.Button(); + this.mainPanel.SuspendLayout(); + this.groupBox2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gwWebServices)).BeginInit(); + this.Application.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.WebServicesMenu.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnSaveChanges); + this.mainPanel.Controls.Add(this.groupBox2); + this.mainPanel.Controls.Add(this.Application); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(633, 507); + this.mainPanel.TabIndex = 4; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.gwWebServices); + this.groupBox2.Location = new System.Drawing.Point(11, 154); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(608, 308); + this.groupBox2.TabIndex = 9; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Current Web Services Of This Application"; + // + // gwWebServices + // + this.gwWebServices.AllowUserToAddRows = false; + this.gwWebServices.AllowUserToDeleteRows = false; + this.gwWebServices.AllowUserToResizeRows = false; + this.gwWebServices.BackgroundColor = System.Drawing.Color.AliceBlue; + this.gwWebServices.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gwWebServices.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.cApplicationName}); + this.gwWebServices.ContextMenuStrip = this.WebServicesMenu; + this.gwWebServices.Location = new System.Drawing.Point(6, 22); + this.gwWebServices.MultiSelect = false; + this.gwWebServices.Name = "gwWebServices"; + this.gwWebServices.ReadOnly = true; + this.gwWebServices.RowHeadersVisible = false; + this.gwWebServices.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.gwWebServices.Size = new System.Drawing.Size(596, 280); + this.gwWebServices.TabIndex = 10; + // + // cApplicationName + // + this.cApplicationName.DataPropertyName = "Url"; + this.cApplicationName.HeaderText = "Web Service URL"; + this.cApplicationName.Name = "cApplicationName"; + this.cApplicationName.ReadOnly = true; + this.cApplicationName.Width = 576; + // + // Application + // + this.Application.Controls.Add(this.lblApplicationListHeader); + this.Application.Controls.Add(this.txtAppName); + this.Application.Location = new System.Drawing.Point(11, 6); + this.Application.Name = "Application"; + this.Application.Size = new System.Drawing.Size(608, 49); + this.Application.TabIndex = 8; + this.Application.TabStop = false; + this.Application.Text = "Application"; + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.LightSlateGray; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(6, 19); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(120, 23); + this.lblApplicationListHeader.TabIndex = 5; + this.lblApplicationListHeader.Text = "Name:"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtAppName + // + this.txtAppName.BackColor = System.Drawing.Color.WhiteSmoke; + this.txtAppName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtAppName.Location = new System.Drawing.Point(127, 19); + this.txtAppName.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtAppName.MaxLength = 50; + this.txtAppName.Name = "txtAppName"; + this.txtAppName.ReadOnly = true; + this.txtAppName.Size = new System.Drawing.Size(242, 23); + this.txtAppName.TabIndex = 6; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.btnAdd); + this.groupBox1.Controls.Add(this.txtServiceUrl); + this.groupBox1.Location = new System.Drawing.Point(11, 61); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(608, 87); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "New Web Service"; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.LightSlateGray; + this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label1.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(6, 23); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(120, 23); + this.label1.TabIndex = 5; + this.label1.Text = "URL:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // btnAdd + // + this.btnAdd.BackColor = System.Drawing.Color.ForestGreen; + this.btnAdd.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnAdd.ForeColor = System.Drawing.Color.White; + this.btnAdd.Location = new System.Drawing.Point(447, 53); + this.btnAdd.Name = "btnAdd"; + this.btnAdd.Size = new System.Drawing.Size(155, 28); + this.btnAdd.TabIndex = 7; + this.btnAdd.Text = "Add Web Service"; + this.btnAdd.UseVisualStyleBackColor = false; + this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click); + // + // txtServiceUrl + // + this.txtServiceUrl.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtServiceUrl.Location = new System.Drawing.Point(127, 23); + this.txtServiceUrl.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtServiceUrl.MaxLength = 50; + this.txtServiceUrl.Name = "txtServiceUrl"; + this.txtServiceUrl.Size = new System.Drawing.Size(475, 23); + this.txtServiceUrl.TabIndex = 6; + // + // WebServicesMenu + // + this.WebServicesMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.removeWebServiceToolStripMenuItem}); + this.WebServicesMenu.Name = "WebServicesMenu"; + this.WebServicesMenu.Size = new System.Drawing.Size(185, 26); + // + // removeWebServiceToolStripMenuItem + // + this.removeWebServiceToolStripMenuItem.Name = "removeWebServiceToolStripMenuItem"; + this.removeWebServiceToolStripMenuItem.Size = new System.Drawing.Size(184, 22); + this.removeWebServiceToolStripMenuItem.Text = "Remove Web Service"; + this.removeWebServiceToolStripMenuItem.Click += new System.EventHandler(this.removeWebServiceToolStripMenuItem_Click); + // + // btnSaveChanges + // + this.btnSaveChanges.BackColor = System.Drawing.Color.ForestGreen; + this.btnSaveChanges.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnSaveChanges.ForeColor = System.Drawing.Color.White; + this.btnSaveChanges.Location = new System.Drawing.Point(458, 468); + this.btnSaveChanges.Name = "btnSaveChanges"; + this.btnSaveChanges.Size = new System.Drawing.Size(155, 28); + this.btnSaveChanges.TabIndex = 10; + this.btnSaveChanges.Text = "Save Changes"; + this.btnSaveChanges.UseVisualStyleBackColor = false; + this.btnSaveChanges.Click += new System.EventHandler(this.btnSaveChanges_Click); + // + // EditApplicationWebServicesForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(633, 507); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.Name = "EditApplicationWebServicesForm"; + this.Text = "Edit Web Services Of Application"; + this.Load += new System.EventHandler(this.EditApplicationWebServicesForm_Load); + this.mainPanel.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gwWebServices)).EndInit(); + this.Application.ResumeLayout(false); + this.Application.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.WebServicesMenu.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.GroupBox Application; + private System.Windows.Forms.Button btnAdd; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtServiceUrl; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.TextBox txtAppName; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.DataGridView gwWebServices; + private System.Windows.Forms.ContextMenuStrip WebServicesMenu; + private System.Windows.Forms.ToolStripMenuItem removeWebServiceToolStripMenuItem; + private System.Windows.Forms.DataGridViewTextBoxColumn cApplicationName; + private System.Windows.Forms.Button btnSaveChanges; + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.cs b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.cs new file mode 100644 index 0000000..05a53f7 --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.cs @@ -0,0 +1,252 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Windows.Forms; +using log4net; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Exceptions; +using MDS.GUI; + +namespace MDS.Management.GUI.ClientApplications +{ + /// + /// This form is used to edit web service communicators of an application. + /// + public partial class EditApplicationWebServicesForm : Form + { + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to MDSController object. + /// All communication with MDS server is performed using this object. + /// + private readonly MDSController _controller; + + /// + /// This list is used as DataSource of data grid. + /// It stores web services of application. + /// + private readonly BindingList _webServicesList; + + #endregion + + #region Constructor + + /// + /// Constructor. + /// + /// Reference to MDSController object + /// Name of the application + public EditApplicationWebServicesForm(MDSController controller, string applicationName) + { + InitializeComponent(); + _controller = controller; + txtAppName.Text = applicationName; + _webServicesList = new BindingList(); + } + + #endregion + + #region Events of Form and controls + + private void EditApplicationWebServicesForm_Load(object sender, EventArgs e) + { + PrepareForm(); + GetWebServiceList(); + } + + private void btnAdd_Click(object sender, EventArgs e) + { + var webServiceUrl = txtServiceUrl.Text.Trim(); + if (string.IsNullOrEmpty(webServiceUrl)) + { + MDSGuiHelper.ShowWarningMessage("Web Service URL can not be empty!"); + return; + } + + _webServicesList.Add(new WebServiceListItem { Url = webServiceUrl }); + gwWebServices.DataSource = _webServicesList; + } + + private void removeWebServiceToolStripMenuItem_Click(object sender, EventArgs e) + { + //Check if user selected any web service to remove + if (gwWebServices.SelectedRows.Count <= 0) + { + return; + } + + var selectedIndex = gwWebServices.SelectedRows[0].Index; + if ((selectedIndex < 0) || (selectedIndex >= _webServicesList.Count)) + { + return; + } + + //Get selected web service item + var selectedItem = _webServicesList[selectedIndex]; + + //Confirm removing + var dialogResult = MDSGuiHelper.ShowQuestionDialog( + "Are you sure to remove web service: " + selectedItem.Url, "Comfirm removing?"); + if (dialogResult != DialogResult.Yes) + { + return; + } + + _webServicesList.RemoveAt(selectedIndex); + } + + private void btnSaveChanges_Click(object sender, EventArgs e) + { + SendChangesToServer(); + } + + #endregion + + #region Private methods + + /// + /// Prepares form. + /// + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + } + + /// + /// Gets web service list of application from web service. + /// + private void GetWebServiceList() + { + try + { + //Send a message to MDS server to get list of web services of application, get response and fill data grid. + var responseMessage = _controller.SendMessageAndGetResponse(new GetApplicationWebServicesMessage {ApplicationName = txtAppName.Text}); + if (responseMessage.MessageTypeId != ControlMessageFactory.MessageTypeIdGetApplicationWebServicesResponseMessage) + { + throw new MDSException("Response message to GetApplicationWebServicesMessage must be a GetApplicationWebServicesResponseMessage"); + } + + var webServicesResponseMessage = responseMessage as GetApplicationWebServicesResponseMessage; + if (webServicesResponseMessage == null) + { + throw new MDSException("Incorrect message type. MessageTypeId = " + responseMessage.MessageTypeId + ", but Type of object: " + responseMessage.GetType().Name); + } + + //Check result + if (!webServicesResponseMessage.Success) + { + MDSGuiHelper.ShowWarningMessage(webServicesResponseMessage.ResultText); + return; + } + + //Fill data grid + FillWebServiceList(webServicesResponseMessage.WebServices); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Error occured while getting web service list from server. Error detail: " + ex.Message); + } + } + + /// + /// Fills data grid (gwWebServices) by list of web services of application. + /// + /// Web service list + private void FillWebServiceList(IEnumerable webServiceInfos) + { + foreach (var webServiceInfo in webServiceInfos) + { + _webServicesList.Add(new WebServiceListItem {Url = webServiceInfo.Url}); + } + + gwWebServices.DataSource = _webServicesList; + } + + /// + /// Sends new web service list to server. + /// + private void SendChangesToServer() + { + try + { + var wsList = new ApplicationWebServiceInfo[_webServicesList.Count]; + for (var i = 0; i < _webServicesList.Count; i++) + { + wsList[i] = new ApplicationWebServiceInfo {Url = _webServicesList[i].Url}; + } + + var responseMessage = _controller.SendMessageAndGetResponse(new UpdateApplicationWebServicesMessage {ApplicationName = txtAppName.Text, WebServices = wsList}); + if (responseMessage.MessageTypeId != ControlMessageFactory.MessageTypeIdOperationResultMessage) + { + throw new MDSException("Response message to UpdateApplicationWebServicesMessage must be a OperationResultMessage"); + } + + var operationResultMessage = responseMessage as OperationResultMessage; + if (operationResultMessage == null) + { + throw new MDSException("Incorrect message type. MessageTypeId = " + responseMessage.MessageTypeId + ", but Type of object: " + responseMessage.GetType().Name); + } + + //Check result + if (!operationResultMessage.Success) + { + MDSGuiHelper.ShowWarningMessage(operationResultMessage.ResultMessage); + return; + } + + //Success + MDSGuiHelper.ShowInfoDialog("Updated web services for application '" + txtAppName.Text + "'.", "Success"); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Error occured while sending web service list to server. Error detail: " + ex.Message); + } + } + + #endregion + + #region Sub classes + + /// + /// Represents an item in web services list. + /// + private class WebServiceListItem + { + /// + /// Url of web service. + /// + public string Url { get; set; } + } + + #endregion + } +} diff --git a/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.resx b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.resx new file mode 100644 index 0000000..2751e7f --- /dev/null +++ b/src/MDSManager/Management/GUI/ClientApplications/EditApplicationWebServicesForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + 17, 17 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ConnectToServerForm.Designer.cs b/src/MDSManager/Management/GUI/ConnectToServerForm.Designer.cs new file mode 100644 index 0000000..300340d --- /dev/null +++ b/src/MDSManager/Management/GUI/ConnectToServerForm.Designer.cs @@ -0,0 +1,160 @@ +namespace MDS.Management.GUI +{ + partial class ConnectToServerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainPanel = new System.Windows.Forms.Panel(); + this.btnConnect = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.txtPort = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.txtIPAddress = new System.Windows.Forms.TextBox(); + this.mainPanel.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnConnect); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(302, 124); + this.mainPanel.TabIndex = 1; + // + // btnConnect + // + this.btnConnect.BackColor = System.Drawing.Color.ForestGreen; + this.btnConnect.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnConnect.ForeColor = System.Drawing.Color.White; + this.btnConnect.Location = new System.Drawing.Point(135, 85); + this.btnConnect.Name = "btnConnect"; + this.btnConnect.Size = new System.Drawing.Size(155, 28); + this.btnConnect.TabIndex = 7; + this.btnConnect.Text = "Connect"; + this.btnConnect.UseVisualStyleBackColor = false; + this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.lblApplicationListHeader); + this.groupBox1.Controls.Add(this.txtPort); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.txtIPAddress); + this.groupBox1.Location = new System.Drawing.Point(8, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(282, 73); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "MDS Server Connection Settings"; + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.LightSlateGray; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(6, 19); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(120, 23); + this.lblApplicationListHeader.TabIndex = 2; + this.lblApplicationListHeader.Text = "IP Address:"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtPort + // + this.txtPort.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtPort.Location = new System.Drawing.Point(127, 43); + this.txtPort.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtPort.MaxLength = 5; + this.txtPort.Name = "txtPort"; + this.txtPort.Size = new System.Drawing.Size(67, 23); + this.txtPort.TabIndex = 10; + this.txtPort.Text = "10905"; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.LightSlateGray; + this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label1.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(6, 43); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(120, 23); + this.label1.TabIndex = 3; + this.label1.Text = "TCP Port:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtIPAddress + // + this.txtIPAddress.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtIPAddress.Location = new System.Drawing.Point(127, 19); + this.txtIPAddress.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtIPAddress.MaxLength = 50; + this.txtIPAddress.Name = "txtIPAddress"; + this.txtIPAddress.Size = new System.Drawing.Size(149, 23); + this.txtIPAddress.TabIndex = 4; + this.txtIPAddress.Text = "127.0.0.1"; + // + // ConnectToServerForm + // + this.AcceptButton = this.btnConnect; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(302, 124); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ConnectToServerForm"; + this.Text = "Connect To MDS Server"; + this.Load += new System.EventHandler(this.ConnectToServerForm_Load); + this.mainPanel.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.TextBox txtIPAddress; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtPort; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Button btnConnect; + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/ConnectToServerForm.cs b/src/MDSManager/Management/GUI/ConnectToServerForm.cs new file mode 100644 index 0000000..b1f70be --- /dev/null +++ b/src/MDSManager/Management/GUI/ConnectToServerForm.cs @@ -0,0 +1,105 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; +using MDS.Communication; +using MDS.GUI; +using MDS.Utils; +using log4net; + +namespace MDS.Management.GUI +{ + /// + /// This form is used to connect a MDS Server. + /// + public partial class ConnectToServerForm : Form + { + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Gets/Sets MDSController that is being connected. + /// + public MDSController MDSController { get; private set; } + + /// + /// This object is used to perform registry operations. + /// + private readonly RegistrySettings _settings; + + /// + /// Constructor. + /// + public ConnectToServerForm() + { + _settings = new RegistrySettings(@"Software\MDS\MDSManager"); + InitializeComponent(); + PrepareForm(); + } + + /// + /// Prepares form. + /// + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + } + + + private void ConnectToServerForm_Load(object sender, EventArgs e) + { + try + { + txtIPAddress.Text = _settings.GetStringValue("LastConnectedIPAddress", "127.0.0.1"); + txtPort.Text = _settings.GetIntegerValue("LastConnectedTCPPort", CommunicationConsts.DefaultMDSPort).ToString(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + private void btnConnect_Click(object sender, EventArgs e) + { + try + { + var port = Convert.ToInt32(txtPort.Text); + MDSController = new MDSController(txtIPAddress.Text, port); + _settings.SetStringValue("LastConnectedIPAddress", txtIPAddress.Text); + _settings.SetIntegerValue("LastConnectedTCPPort", port); + Close(); + } + catch + { + MDSGuiHelper.ShowErrorMessage("Please check IP address and TCP Port. TCP port must be numeric."); + } + } + } +} diff --git a/src/MDSManager/Management/GUI/ConnectToServerForm.resx b/src/MDSManager/Management/GUI/ConnectToServerForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/src/MDSManager/Management/GUI/ConnectToServerForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.Designer.cs b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.Designer.cs new file mode 100644 index 0000000..358fa2c --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.Designer.cs @@ -0,0 +1,308 @@ +namespace MDS.Management.GUI.MDSServers +{ + partial class EditRouteForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainPanel = new System.Windows.Forms.Panel(); + this.btnUpdate = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.cApplication = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.cRouteFactor = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cmbRouteType = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.gwWebServices = new System.Windows.Forms.DataGridView(); + this.cApplicationName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.cDestDestinationServer = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Column3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.mainPanel.SuspendLayout(); + this.groupBox2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + this.groupBox3.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gwWebServices)).BeginInit(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnUpdate); + this.mainPanel.Controls.Add(this.groupBox2); + this.mainPanel.Controls.Add(this.groupBox3); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(955, 517); + this.mainPanel.TabIndex = 3; + // + // btnUpdate + // + this.btnUpdate.BackColor = System.Drawing.Color.ForestGreen; + this.btnUpdate.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnUpdate.ForeColor = System.Drawing.Color.White; + this.btnUpdate.Location = new System.Drawing.Point(782, 478); + this.btnUpdate.Name = "btnUpdate"; + this.btnUpdate.Size = new System.Drawing.Size(155, 28); + this.btnUpdate.TabIndex = 10; + this.btnUpdate.Text = "OK"; + this.btnUpdate.UseVisualStyleBackColor = false; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.dataGridView1); + this.groupBox2.Location = new System.Drawing.Point(8, 240); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(935, 232); + this.groupBox2.TabIndex = 9; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Route Destinations"; + // + // dataGridView1 + // + this.dataGridView1.AllowUserToAddRows = false; + this.dataGridView1.AllowUserToDeleteRows = false; + this.dataGridView1.AllowUserToResizeRows = false; + this.dataGridView1.BackgroundColor = System.Drawing.Color.AliceBlue; + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dataGridViewTextBoxColumn1, + this.cApplication, + this.cRouteFactor}); + this.dataGridView1.Location = new System.Drawing.Point(6, 22); + this.dataGridView1.MultiSelect = false; + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.ReadOnly = true; + this.dataGridView1.RowHeadersVisible = false; + this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dataGridView1.Size = new System.Drawing.Size(923, 202); + this.dataGridView1.TabIndex = 11; + // + // dataGridViewTextBoxColumn1 + // + this.dataGridViewTextBoxColumn1.DataPropertyName = "Url"; + this.dataGridViewTextBoxColumn1.HeaderText = "Destination Server Name"; + this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; + this.dataGridViewTextBoxColumn1.ReadOnly = true; + this.dataGridViewTextBoxColumn1.Width = 350; + // + // cApplication + // + this.cApplication.HeaderText = "Destination Application Name"; + this.cApplication.Name = "cApplication"; + this.cApplication.ReadOnly = true; + this.cApplication.Width = 430; + // + // cRouteFactor + // + this.cRouteFactor.HeaderText = "Route Factor"; + this.cRouteFactor.Name = "cRouteFactor"; + this.cRouteFactor.ReadOnly = true; + this.cRouteFactor.Width = 123; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.label1); + this.groupBox3.Controls.Add(this.cmbRouteType); + this.groupBox3.Controls.Add(this.label2); + this.groupBox3.Controls.Add(this.txtName); + this.groupBox3.Location = new System.Drawing.Point(8, 5); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(935, 76); + this.groupBox3.TabIndex = 8; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Route Properties"; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.LightSlateGray; + this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label1.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(6, 45); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(120, 23); + this.label1.TabIndex = 15; + this.label1.Text = "Route Type:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // cmbRouteType + // + this.cmbRouteType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbRouteType.FormattingEnabled = true; + this.cmbRouteType.Items.AddRange(new object[] { + "Sequential", + "Random"}); + this.cmbRouteType.Location = new System.Drawing.Point(127, 45); + this.cmbRouteType.Name = "cmbRouteType"; + this.cmbRouteType.Size = new System.Drawing.Size(146, 24); + this.cmbRouteType.TabIndex = 14; + // + // label2 + // + this.label2.BackColor = System.Drawing.Color.LightSlateGray; + this.label2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label2.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label2.ForeColor = System.Drawing.Color.White; + this.label2.Location = new System.Drawing.Point(6, 21); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(120, 23); + this.label2.TabIndex = 13; + this.label2.Text = "Route Name:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtName + // + this.txtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtName.Location = new System.Drawing.Point(127, 21); + this.txtName.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtName.MaxLength = 50; + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(146, 23); + this.txtName.TabIndex = 12; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.gwWebServices); + this.groupBox1.Location = new System.Drawing.Point(8, 87); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(935, 147); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Route Filters"; + // + // gwWebServices + // + this.gwWebServices.AllowUserToAddRows = false; + this.gwWebServices.AllowUserToDeleteRows = false; + this.gwWebServices.AllowUserToResizeRows = false; + this.gwWebServices.BackgroundColor = System.Drawing.Color.AliceBlue; + this.gwWebServices.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gwWebServices.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.cApplicationName, + this.Column1, + this.cDestDestinationServer, + this.Column2, + this.Column3}); + this.gwWebServices.Location = new System.Drawing.Point(6, 22); + this.gwWebServices.MultiSelect = false; + this.gwWebServices.Name = "gwWebServices"; + this.gwWebServices.ReadOnly = true; + this.gwWebServices.RowHeadersVisible = false; + this.gwWebServices.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.gwWebServices.Size = new System.Drawing.Size(923, 117); + this.gwWebServices.TabIndex = 11; + // + // cApplicationName + // + this.cApplicationName.DataPropertyName = "Url"; + this.cApplicationName.HeaderText = "Source Server"; + this.cApplicationName.Name = "cApplicationName"; + this.cApplicationName.ReadOnly = true; + this.cApplicationName.Width = 195; + // + // Column1 + // + this.Column1.HeaderText = "Source Application"; + this.Column1.Name = "Column1"; + this.Column1.ReadOnly = true; + this.Column1.Width = 195; + // + // cDestDestinationServer + // + this.cDestDestinationServer.HeaderText = "Destination Server"; + this.cDestDestinationServer.Name = "cDestDestinationServer"; + this.cDestDestinationServer.ReadOnly = true; + this.cDestDestinationServer.Width = 195; + // + // Column2 + // + this.Column2.HeaderText = "Destination Application"; + this.Column2.Name = "Column2"; + this.Column2.ReadOnly = true; + this.Column2.Width = 195; + // + // Column3 + // + this.Column3.HeaderText = "Transmit Rule"; + this.Column3.Name = "Column3"; + this.Column3.ReadOnly = true; + this.Column3.Width = 123; + // + // EditRouteForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(955, 517); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.MaximizeBox = false; + this.Name = "EditRouteForm"; + this.Text = "Add / Edit Route Form"; + this.mainPanel.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.groupBox1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gwWebServices)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtName; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox cmbRouteType; + private System.Windows.Forms.DataGridView gwWebServices; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.Button btnUpdate; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; + private System.Windows.Forms.DataGridViewTextBoxColumn cApplication; + private System.Windows.Forms.DataGridViewTextBoxColumn cRouteFactor; + private System.Windows.Forms.DataGridViewTextBoxColumn cApplicationName; + private System.Windows.Forms.DataGridViewTextBoxColumn Column1; + private System.Windows.Forms.DataGridViewTextBoxColumn cDestDestinationServer; + private System.Windows.Forms.DataGridViewTextBoxColumn Column2; + private System.Windows.Forms.DataGridViewTextBoxColumn Column3; + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.cs b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.cs new file mode 100644 index 0000000..67e2411 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.cs @@ -0,0 +1,39 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace MDS.Management.GUI.MDSServers +{ + public partial class EditRouteForm : Form + { + public EditRouteForm() + { + InitializeComponent(); + cmbRouteType.SelectedIndex = 0; + } + } +} diff --git a/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.resx b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.resx new file mode 100644 index 0000000..08088c8 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditRouteForm.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/EditServerForm.Designer.cs b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.Designer.cs new file mode 100644 index 0000000..5fc43f9 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.Designer.cs @@ -0,0 +1,188 @@ +namespace MDS.Management.GUI.MDSServers +{ + partial class EditServerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainPanel = new System.Windows.Forms.Panel(); + this.btnUpdate = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label2 = new System.Windows.Forms.Label(); + this.txtName = new System.Windows.Forms.TextBox(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.txtPort = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.txtIPAddress = new System.Windows.Forms.TextBox(); + this.mainPanel.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.btnUpdate); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(302, 150); + this.mainPanel.TabIndex = 2; + // + // btnUpdate + // + this.btnUpdate.BackColor = System.Drawing.Color.ForestGreen; + this.btnUpdate.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnUpdate.ForeColor = System.Drawing.Color.White; + this.btnUpdate.Location = new System.Drawing.Point(135, 112); + this.btnUpdate.Name = "btnUpdate"; + this.btnUpdate.Size = new System.Drawing.Size(155, 28); + this.btnUpdate.TabIndex = 4; + this.btnUpdate.Text = "Update"; + this.btnUpdate.UseVisualStyleBackColor = false; + this.btnUpdate.Click += new System.EventHandler(this.btnUpdate_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.txtName); + this.groupBox1.Controls.Add(this.lblApplicationListHeader); + this.groupBox1.Controls.Add(this.txtPort); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.txtIPAddress); + this.groupBox1.Location = new System.Drawing.Point(8, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(282, 100); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "MDS Server Properties"; + // + // label2 + // + this.label2.BackColor = System.Drawing.Color.LightSlateGray; + this.label2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label2.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label2.ForeColor = System.Drawing.Color.White; + this.label2.Location = new System.Drawing.Point(6, 22); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(120, 23); + this.label2.TabIndex = 11; + this.label2.Text = "Name:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtName + // + this.txtName.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtName.Location = new System.Drawing.Point(127, 22); + this.txtName.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtName.MaxLength = 50; + this.txtName.Name = "txtName"; + this.txtName.Size = new System.Drawing.Size(149, 23); + this.txtName.TabIndex = 1; + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.LightSlateGray; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(6, 46); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(120, 23); + this.lblApplicationListHeader.TabIndex = 2; + this.lblApplicationListHeader.Text = "IP Address:"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtPort + // + this.txtPort.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtPort.Location = new System.Drawing.Point(127, 70); + this.txtPort.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtPort.MaxLength = 5; + this.txtPort.Name = "txtPort"; + this.txtPort.Size = new System.Drawing.Size(67, 23); + this.txtPort.TabIndex = 3; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.LightSlateGray; + this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label1.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(6, 70); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(120, 23); + this.label1.TabIndex = 3; + this.label1.Text = "TCP Port:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtIPAddress + // + this.txtIPAddress.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtIPAddress.Location = new System.Drawing.Point(127, 46); + this.txtIPAddress.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtIPAddress.MaxLength = 50; + this.txtIPAddress.Name = "txtIPAddress"; + this.txtIPAddress.Size = new System.Drawing.Size(149, 23); + this.txtIPAddress.TabIndex = 2; + // + // EditServerForm + // + this.AcceptButton = this.btnUpdate; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(302, 150); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EditServerForm"; + this.Text = "Server Properties"; + this.Load += new System.EventHandler(this.EditServerForm_Load); + this.mainPanel.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.Button btnUpdate; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.TextBox txtPort; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtIPAddress; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtName; + + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/EditServerForm.cs b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.cs new file mode 100644 index 0000000..54944b1 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.cs @@ -0,0 +1,137 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using MDS.Communication.Messages.ControllerMessages; +using MDS.GUI; + +namespace MDS.Management.GUI.MDSServers +{ + /// + /// This form is used to view/change properties of a server in graph. + /// + public partial class EditServerForm : Form + { + /// + /// True, if user accepted changes by pressing Update button. + /// + public bool Updated { get; private set; } + + /// + /// Server to edit. + /// + private readonly ServerGraphInfo.ServerOnGraph _server; + + /// + /// True, if adding new server, else updating exists server. + /// + private readonly bool _addNew; + + /// + /// Constructor. + /// + /// Server to edit + /// + public EditServerForm(ServerGraphInfo.ServerOnGraph server, bool addNew) + { + InitializeComponent(); + _server = server; + _addNew = addNew; + txtName.Text = _server.Name; + txtIPAddress.Text = _server.IpAddress; + txtPort.Text = _server.Port.ToString(); + } + + private void EditServerForm_Load(object sender, EventArgs e) + { + PrepareForm(); + } + + /// + /// Prepares form. This method is called while form is opening. + /// + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + if (_addNew) + { + Text = "Add New Server"; + btnUpdate.Text = "Add Server"; + } + } + + private void btnUpdate_Click(object sender, EventArgs e) + { + var name = txtName.Text; + var ipAddress = txtIPAddress.Text; + var port = txtPort.Text; + + if (string.IsNullOrEmpty(name)) + { + MDSGuiHelper.ShowWarningMessage("Server name can not be empty.", "Server name is invalid!"); + return; + } + + if (string.IsNullOrEmpty(ipAddress)) + { + MDSGuiHelper.ShowWarningMessage("IP address can not be empty.", "IP address is invalid!"); + return; + } + + if (string.IsNullOrEmpty(port)) + { + MDSGuiHelper.ShowWarningMessage("TCP Port can not be empty.", "TCP Port is invalid!"); + return; + } + + int portNo; + try + { + portNo = Convert.ToInt32(port); + } + catch + { + MDSGuiHelper.ShowWarningMessage("TCP Port must be numeric.", "TCP Port is invalid!"); + return; + } + + if(portNo <= 0) + { + MDSGuiHelper.ShowWarningMessage("TCP Port must be a positive number.", "TCP Port is invalid!"); + return; + } + + _server.Name = name; + _server.IpAddress = ipAddress; + _server.Port = portNo; + + Updated = true; + + Close(); + } + } +} diff --git a/src/MDSManager/Management/GUI/MDSServers/EditServerForm.resx b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/EditServerForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/RoutesForm.Designer.cs b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.Designer.cs new file mode 100644 index 0000000..c5135c0 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.Designer.cs @@ -0,0 +1,46 @@ +namespace MDS.Management.GUI.MDSServers +{ + partial class RoutesForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // RoutesForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(710, 318); + this.Name = "RoutesForm"; + this.Text = "RoutesForm"; + this.ResumeLayout(false); + + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/RoutesForm.cs b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.cs new file mode 100644 index 0000000..8df04e5 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.cs @@ -0,0 +1,38 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace MDS.Management.GUI.MDSServers +{ + public partial class RoutesForm : Form + { + public RoutesForm() + { + InitializeComponent(); + } + } +} diff --git a/src/MDSManager/Management/GUI/MDSServers/RoutesForm.resx b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/RoutesForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.Designer.cs b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.Designer.cs new file mode 100644 index 0000000..360b784 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.Designer.cs @@ -0,0 +1,164 @@ +using MDS.Management.GUI.Tools; + +namespace MDS.Management.GUI.MDSServers +{ + partial class ServerGraphForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.mainPanel = new System.Windows.Forms.Panel(); + this.pnlDesign = new MDS.Management.GUI.Tools.DoubleBufferedPanel(); + this.cmsDesignAreaRightMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.addNewServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.btnSaveGraph = new System.Windows.Forms.Button(); + this.cmsServerRightMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.removeServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.setAsThisServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.serverPropertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mainPanel.SuspendLayout(); + this.cmsDesignAreaRightMenu.SuspendLayout(); + this.cmsServerRightMenu.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.pnlDesign); + this.mainPanel.Controls.Add(this.btnSaveGraph); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(894, 572); + this.mainPanel.TabIndex = 2; + // + // pnlDesign + // + this.pnlDesign.AutoScroll = true; + this.pnlDesign.AutoScrollMargin = new System.Drawing.Size(5, 5); + this.pnlDesign.BackColor = System.Drawing.Color.AliceBlue; + this.pnlDesign.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pnlDesign.ContextMenuStrip = this.cmsDesignAreaRightMenu; + this.pnlDesign.Location = new System.Drawing.Point(3, 3); + this.pnlDesign.Name = "pnlDesign"; + this.pnlDesign.Size = new System.Drawing.Size(886, 528); + this.pnlDesign.TabIndex = 4; + this.pnlDesign.Paint += new System.Windows.Forms.PaintEventHandler(this.DesignPanel_Paint); + this.pnlDesign.MouseDown += new System.Windows.Forms.MouseEventHandler(this.DesignPanel_MouseDown); + // + // cmsDesignAreaRightMenu + // + this.cmsDesignAreaRightMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.addNewServerToolStripMenuItem}); + this.cmsDesignAreaRightMenu.Name = "cmsDesignAreaRightMenu"; + this.cmsDesignAreaRightMenu.Size = new System.Drawing.Size(159, 26); + // + // addNewServerToolStripMenuItem + // + this.addNewServerToolStripMenuItem.Name = "addNewServerToolStripMenuItem"; + this.addNewServerToolStripMenuItem.Size = new System.Drawing.Size(158, 22); + this.addNewServerToolStripMenuItem.Text = "Add New Server"; + this.addNewServerToolStripMenuItem.Click += new System.EventHandler(this.DesignArea_AddNewServer_Click); + // + // btnSaveGraph + // + this.btnSaveGraph.BackColor = System.Drawing.Color.ForestGreen; + this.btnSaveGraph.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnSaveGraph.ForeColor = System.Drawing.Color.White; + this.btnSaveGraph.Location = new System.Drawing.Point(689, 536); + this.btnSaveGraph.Name = "btnSaveGraph"; + this.btnSaveGraph.Size = new System.Drawing.Size(200, 28); + this.btnSaveGraph.TabIndex = 3; + this.btnSaveGraph.Text = "Save && Update Graph"; + this.btnSaveGraph.UseVisualStyleBackColor = false; + this.btnSaveGraph.Click += new System.EventHandler(this.btnSaveGraph_Click); + // + // cmsServerRightMenu + // + this.cmsServerRightMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.removeServerToolStripMenuItem, + this.setAsThisServerToolStripMenuItem, + this.serverPropertiesToolStripMenuItem}); + this.cmsServerRightMenu.Name = "cmsServerRightMenu"; + this.cmsServerRightMenu.Size = new System.Drawing.Size(175, 70); + // + // removeServerToolStripMenuItem + // + this.removeServerToolStripMenuItem.Name = "removeServerToolStripMenuItem"; + this.removeServerToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + this.removeServerToolStripMenuItem.Text = "Remove Server"; + this.removeServerToolStripMenuItem.Click += new System.EventHandler(this.ServerRightMenu_RemoveServer_Click); + // + // setAsThisServerToolStripMenuItem + // + this.setAsThisServerToolStripMenuItem.Name = "setAsThisServerToolStripMenuItem"; + this.setAsThisServerToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + this.setAsThisServerToolStripMenuItem.Text = "Set As This Server"; + this.setAsThisServerToolStripMenuItem.Click += new System.EventHandler(this.ServerRightMenu_SetAsThisServer_Click); + // + // serverPropertiesToolStripMenuItem + // + this.serverPropertiesToolStripMenuItem.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold); + this.serverPropertiesToolStripMenuItem.Name = "serverPropertiesToolStripMenuItem"; + this.serverPropertiesToolStripMenuItem.Size = new System.Drawing.Size(174, 22); + this.serverPropertiesToolStripMenuItem.Text = "Server Properties"; + this.serverPropertiesToolStripMenuItem.Click += new System.EventHandler(this.ServerRightMenu_ServerProperties_Click); + // + // ServerGraphForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(894, 572); + this.Controls.Add(this.mainPanel); + this.DoubleBuffered = true; + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.Margin = new System.Windows.Forms.Padding(4); + this.Name = "ServerGraphForm"; + this.Text = "DotNetMQ Server Graph"; + this.Load += new System.EventHandler(this.ServerGraphForm_Load); + this.Resize += new System.EventHandler(this.ServerGraphForm_Resize); + this.mainPanel.ResumeLayout(false); + this.cmsDesignAreaRightMenu.ResumeLayout(false); + this.cmsServerRightMenu.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.Button btnSaveGraph; + private DoubleBufferedPanel pnlDesign; + private System.Windows.Forms.ContextMenuStrip cmsServerRightMenu; + private System.Windows.Forms.ToolStripMenuItem removeServerToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem serverPropertiesToolStripMenuItem; + private System.Windows.Forms.ContextMenuStrip cmsDesignAreaRightMenu; + private System.Windows.Forms.ToolStripMenuItem addNewServerToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem setAsThisServerToolStripMenuItem; + } +} \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.cs b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.cs new file mode 100644 index 0000000..9e3d180 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.cs @@ -0,0 +1,897 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; +using log4net; +using MDS.Communication; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Exceptions; +using MDS.GUI; + +namespace MDS.Management.GUI.MDSServers +{ + /// + /// This form is used to design Server graph of MDS. + /// + public partial class ServerGraphForm : Form + { + #region Private properties + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Reference to MDSController object. + /// All communication with MDS server is performed using this object. + /// + private readonly MDSController _controller; + + /// + /// Reference to this MDS server on graph. + /// + private MDSServer _thisServer; + + /// + /// All MDS servers on graph. + /// + private readonly List _servers; + + /// + /// Stores all graph information that are gotten from MDS server. + /// + private ServerGraphInfo _serverGraphInfo; + + #region Designing fields + + /// + /// This is the first clicked label to create a link between servers + /// + private Label _selectedLabel; + + /// + /// X coordinate of mouse in selected label. + /// + private int _movingLabelOffsetX; + + /// + /// Y coordinate of mouse in selected label. + /// + private int _movingLabelOffsetY; + + /// + /// X coordinate of label that is guiding for replacement to _movingLabel. + /// + private int _movingLabelGuideLabelX = -1; + + /// + /// Y coordinate of label that is guiding for replacement to _movingLabel. + /// + private int _movingLabelGuideLabelY = -1; + + /// + /// X coordinate of moouse at the last click in the design area. + /// It is used to find initial location when adding new server. + /// + private int _designPanelLastX = -1; + + /// + /// Y coordinate of moouse at the last click in the design area. + /// It is used to find initial location when adding new server. + /// + private int _designPanelLastY = -1; + + #endregion + + #region Drawing fields + + /// + /// This pen is used to draw lines between servers. + /// + private readonly Pen _linePen; + + /// + /// This pen is used to draw lines when moving a label for guide lines. + /// + private readonly Pen _guideLinePen; + + /// + /// This pen is used to select (to draw a outer rectangle to) a server label. + /// + private readonly Pen _labelSelectingPen; + + /// + /// Background color of label of this server on graph. + /// + private readonly Color _thisServerBackColor; + + /// + /// Background color of label of all servers on graph except this server. + /// + private readonly Color _serverBackColor; + + #endregion + + #endregion + + #region Constructor + + /// + /// Creates a new ServerGraphForm. + /// + /// Reference to MDSController object + public ServerGraphForm(MDSController controller) + { + _controller = controller; + _servers = new List(); + _linePen = new Pen(Brushes.Black, 1.0f); + _guideLinePen = new Pen(Color.LightGray, 1.0f); + _labelSelectingPen = new Pen(Color.Black, 1.0f) { DashStyle = DashStyle.DashDot }; + _thisServerBackColor = Color.Crimson; + _serverBackColor = Color.DodgerBlue; + InitializeComponent(); + } + + #endregion + + #region Form events + + private void ServerGraphForm_Load(object sender, EventArgs e) + { + try + { + PrepareForm(); + GetGraphFromServer(); + } + catch (Exception ex) + { + MessageBox.Show("Can not load graph. " + ex.Message); + Close(); + } + } + + private void btnSaveGraph_Click(object sender, EventArgs e) + { + btnSaveGraph.Enabled = false; + Application.DoEvents(); + + try + { + CheckGraph(); + _serverGraphInfo = CreateServerGraphInfo(); + + //Send message to the server and get response + var message = _controller.SendMessageAndGetResponse( + new UpdateServerGraphMessage {ServerGraph = _serverGraphInfo} + ); + + //Check response message + if (message.MessageTypeId != ControlMessageFactory.MessageTypeIdOperationResultMessage) + { + throw new MDSException("Response message to UpdateServerGraphMessage must be a OperationResultMessage"); + } + + var updateResponseMessage = message as OperationResultMessage; + if (updateResponseMessage == null) + { + throw new MDSException("Incorrect message type. MessageTypeId = " + message.MessageTypeId + ", but Type of object: " + message.GetType().Name); + } + + //Inform user about update result + if (updateResponseMessage.Success) + { + MDSGuiHelper.ShowInfoDialog("Server graph is successfully updated on server", "Success."); + } + else + { + MDSGuiHelper.ShowErrorMessage( + "Server graph can not be updated on server. Reason: " + updateResponseMessage.ResultMessage, + "Failed!"); + } + } + catch (Exception ex) + { + MDSGuiHelper.ShowWarningMessage("Can not save graph. " + ex.Message); + } + finally + { + btnSaveGraph.Enabled = true; + } + } + + private void ServerGraphForm_Resize(object sender, EventArgs e) + { + pnlDesign.Width = Width - 24; + pnlDesign.Height = Height - 82; + btnSaveGraph.Left = pnlDesign.Right - 200; + btnSaveGraph.Top = pnlDesign.Bottom + 5; + } + + private void ServerLabel_MouseDown(object sender, MouseEventArgs e) + { + var currentLabel = sender as Label; + if (currentLabel == null) + { + return; + } + + if (e.Button == MouseButtons.Left) + { + if (((ModifierKeys & Keys.Control) == Keys.Control) && (_selectedLabel != null)) + { + LinkOrUnlinkServers(_selectedLabel, currentLabel); + } + + currentLabel.BackColor = Color.Black; + _movingLabelOffsetX = e.X; + _movingLabelOffsetY = e.Y; + } + + _selectedLabel = currentLabel; + pnlDesign.Invalidate(); + } + + private void ServerLabel_MouseMove(object sender, MouseEventArgs e) + { + if ((_selectedLabel != null) && (e.Button == MouseButtons.Left)) + { + //Find normal X and Y ccordinate of mouse + var labelX = e.X + _selectedLabel.Left - _movingLabelOffsetX; + var labelY = e.Y + _selectedLabel.Top - _movingLabelOffsetY; + + //If Alt key is not pressed and there is a label whose coordinates are (5 pixel) close to this label's, than + //hold server label, else move label... + _movingLabelGuideLabelX = ((ModifierKeys & Keys.Alt) == Keys.Alt) ? int.MinValue : FindGuideLabelX(labelX); + _selectedLabel.Left = (_movingLabelGuideLabelX > int.MinValue) ? _movingLabelGuideLabelX : labelX; + _movingLabelGuideLabelY = ((ModifierKeys & Keys.Alt) == Keys.Alt) ? int.MinValue : FindGuideLabelY(labelY); + _selectedLabel.Top = (_movingLabelGuideLabelY > int.MinValue) ? _movingLabelGuideLabelY : labelY; + + pnlDesign.Invalidate(); + } + } + + private void ServerLabel_MouseUp(object sender, MouseEventArgs e) + { + if (_selectedLabel != null && e.Button == MouseButtons.Left) + { + _selectedLabel.BackColor = (_thisServer != null && _selectedLabel == _thisServer.LabelOfServer) + ? _thisServerBackColor + : _serverBackColor; + _movingLabelGuideLabelX = int.MinValue; + _movingLabelGuideLabelY = int.MinValue; + pnlDesign.Invalidate(); + } + } + + private void DesignPanel_MouseDown(object sender, MouseEventArgs e) + { + _designPanelLastX = e.X; + _designPanelLastY = e.Y; + + if (_selectedLabel == null) + { + return; + } + + _selectedLabel = null; + pnlDesign.Invalidate(); + } + + private void ServerLabel_MouseDoubleClick(object sender, MouseEventArgs e) + { + ServerRightMenu_ServerProperties_Click(sender, e); + } + + private void DesignPanel_Paint(object sender, PaintEventArgs e) + { + DrawAllLines(e.Graphics); + } + + private void ServerRightMenu_RemoveServer_Click(object sender, EventArgs e) + { + var removingServer = GetSelectedServer(); + if (removingServer == null) + { + return; + } + + if (removingServer.ServerInfo.Name == _thisServer.ServerInfo.Name) + { + MDSGuiHelper.ShowWarningMessage("You can not remove this server from graph!", "Attention!"); + return; + } + + var dialogResult = MDSGuiHelper.ShowQuestionDialog( + "Are you sure to remove MDS Server " + removingServer.ServerInfo.Name + " (" + + removingServer.ServerInfo.IpAddress + ") from graph.", "Confirm removing server!"); + if (dialogResult != DialogResult.Yes) + { + return; + } + + foreach (var adjacent in removingServer.Adjacents) + { + if (adjacent.Adjacents.Contains(removingServer)) + { + adjacent.Adjacents.Remove(removingServer); + } + } + + _servers.Remove(removingServer); + pnlDesign.Controls.Remove(removingServer.LabelOfServer); + _selectedLabel = null; + + pnlDesign.Invalidate(); + } + + private void ServerRightMenu_SetAsThisServer_Click(object sender, EventArgs e) + { + var newThisServer = GetSelectedServer(); + if (newThisServer == null || newThisServer == _thisServer) + { + return; + } + + if (_thisServer != null) + { + _thisServer.LabelOfServer.BackColor = _serverBackColor; + } + + _thisServer = newThisServer; + _thisServer.LabelOfServer.BackColor = _thisServerBackColor; + } + + private void ServerRightMenu_ServerProperties_Click(object sender, EventArgs e) + { + var editingServer = GetSelectedServer(); + if (editingServer == null) + { + return; + } + + var editServerForm = new EditServerForm(editingServer.ServerInfo, false); + editServerForm.ShowDialog(); + if (!editServerForm.Updated) + { + return; + } + + editingServer.LabelOfServer.Text = editingServer.ServerInfo.Name + Environment.NewLine + editingServer.ServerInfo.IpAddress; + pnlDesign.Invalidate(); + } + + private void DesignArea_AddNewServer_Click(object sender, EventArgs e) + { + var newServer = new MDSServer + { + ServerInfo = + new ServerGraphInfo.ServerOnGraph + { + Name = "", + IpAddress = "", + Port = CommunicationConsts.DefaultMDSPort, + Adjacents = "", + Location = _designPanelLastX + "," + _designPanelLastY, + } + }; + + var addServerForm = new EditServerForm(newServer.ServerInfo, true); + addServerForm.ShowDialog(); + if (!addServerForm.Updated) + { + return; + } + + newServer.LabelOfServer = CreateServerLabel(newServer); + newServer.LabelOfServer.Text = newServer.ServerInfo.Name + Environment.NewLine + newServer.ServerInfo.IpAddress; + newServer.LabelOfServer.Left = _designPanelLastX; + newServer.LabelOfServer.Top = _designPanelLastY; + _servers.Add(newServer); + pnlDesign.Invalidate(); + } + + #endregion + + #region Private methods + + private void PrepareForm() + { + Left = (Screen.GetWorkingArea(this).Width - Width) / 2; + Top = (Screen.GetWorkingArea(this).Height - Height) / 2; + } + + /// + /// Gets server graph from server and creates graph on screen. + /// + private void GetGraphFromServer() + { + try + { + //Send a message to MDS server to get list of client applications, get response and fill data grid. + var message = _controller.SendMessageAndGetResponse(new GetServerGraphMessage()); + if (message.MessageTypeId != ControlMessageFactory.MessageTypeIdGetServerGraphResponseMessage) + { + throw new MDSException("Response message to GetServerGraphMessage must be a GetServerGraphResponseMessage"); + } + + var serverGraphResponseMessage = message as GetServerGraphResponseMessage; + if (serverGraphResponseMessage == null) + { + throw new MDSException("Incorrect message type. MessageTypeId = " + message.MessageTypeId + ", but Type of object: " + message.GetType().Name); + } + + _serverGraphInfo = serverGraphResponseMessage.ServerGraph; + CreateGraph(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Application list can not received from MDS Server. Detail: " + ex.Message); + } + } + + /// + /// Creates graph on design area from _serverGraphInfo. + /// + private void CreateGraph() + { + //Create MDSServerNode objects and get names of adjacents of nodes + var adjacentsOfServers = new SortedList(); + foreach (var server in _serverGraphInfo.Servers) + { + var mdsServer = new MDSServer + { + ServerInfo = server, + }; + _servers.Add(mdsServer); + adjacentsOfServers.Add(server.Name, server.Adjacents); + if (server.Name == _serverGraphInfo.ThisServerName) + { + _thisServer = mdsServer; + } + } + + //Set Adjacent servers + foreach (var mdsServer in _servers) + { + //Get adjacent names + var adjacents = adjacentsOfServers[mdsServer.ServerInfo.Name].Split(','); + //Add nodes as adjacent + foreach (var adjacent in adjacents) + { + var trimmedAdjacentName = adjacent.Trim(); + if (string.IsNullOrEmpty(trimmedAdjacentName)) + { + continue; + } + + var adjacentServer = FindServer(trimmedAdjacentName); + if (adjacentServer == null) + { + continue; + } + + mdsServer.Adjacents.Add(adjacentServer); + } + } + + //Create labels for servers + foreach (var server in _servers) + { + server.LabelOfServer = CreateServerLabel(server); + } + + //Set design properties + foreach (var server in _serverGraphInfo.Servers) + { + var mdsServer = FindServer(server.Name); + if (mdsServer == null) + { + continue; + } + + var splittedLocation = server.Location.Split(new[] { ',', ';' }); + mdsServer.LabelOfServer.Left = Convert.ToInt32(splittedLocation[0]); + mdsServer.LabelOfServer.Top = Convert.ToInt32(splittedLocation[1]); + } + } + + /// + /// Checks graph if it is a valid graph. + /// + private void CheckGraph() + { + //Check if This Server is defined + if (_thisServer == null || FindServer(_thisServer.ServerInfo.Name) == null) + { + throw new MDSException("This server is not defined in the graph."); + } + + //Return, if there is no server except this server + if (_servers.Count == 1) + { + return; + } + + //Check if there is an unconnected server exist on graph + foreach (var server in _servers) + { + if (server.Adjacents.Count <= 0) + { + throw new MDSException("MDS Server '" + server.ServerInfo.Name + "' has no connection to other servers on graph."); + } + + if (!IsThereAPath(_thisServer, server, new List())) + { + throw new MDSException("There is no path from this MDS server (" + + _thisServer.ServerInfo.Name + ") to '" + server.ServerInfo.Name + "'."); + } + } + } + + /// + /// Prepares ServerGraphInfo object from graph on screen. + /// + /// ServerGraphInfo object + private ServerGraphInfo CreateServerGraphInfo() + { + var graphInfo = new ServerGraphInfo + { + ThisServerName = _thisServer.ServerInfo.Name, + Servers = new ServerGraphInfo.ServerOnGraph[_servers.Count] + }; + for (var i = 0; i < _servers.Count; i++) + { + graphInfo.Servers[i] = + new ServerGraphInfo.ServerOnGraph + { + Name = _servers[i].ServerInfo.Name, + IpAddress = _servers[i].ServerInfo.IpAddress, + Port = _servers[i].ServerInfo.Port, + Location = _servers[i].LabelOfServer.Left + "," + _servers[i].LabelOfServer.Top, + Adjacents = GetAdjacentListAsString(_servers[i]) + }; + } + + return graphInfo; + } + + /// + /// Creates a comma delimited string list from adjacent names of given server. + /// + /// Server to get adjacent list + /// Adjacent list as string + private static string GetAdjacentListAsString(MDSServer server) + { + var adjacentList = new StringBuilder(); + for (var i = 0; i < server.Adjacents.Count; i++) + { + if (i > 0) + { + adjacentList.Append(","); + } + + adjacentList.Append(server.Adjacents[i].ServerInfo.Name); + } + + return adjacentList.ToString(); + } + + /// + /// Checks if there is a path from a MDS server to another. + /// I runs as recursive and searches all nodes to find a path from source to destination. + /// + /// Source server of path + /// Destination server of path + /// List of all passed servers until now + /// True, if there is a path + private static bool IsThereAPath(MDSServer sourceServer, MDSServer destServer, ICollection passedServers) + { + if (passedServers.Contains(sourceServer)) + { + return false; + } + + if (sourceServer.Adjacents.Contains(destServer)) + { + return true; + } + + passedServers.Add(sourceServer); + + foreach (var adjacent in sourceServer.Adjacents) + { + if(IsThereAPath(adjacent, destServer, passedServers)) + { + return true; + } + } + + return false; + } + + /// + /// Finds a server in Servers list by name. + /// + /// Name of server + /// Found server or null if can not find + private MDSServer FindServer(string name) + { + foreach (var server in _servers) + { + if (server.ServerInfo.Name == name) + { + return server; + } + } + + return null; + } + + /// + /// Finds a server in Servers list by Label. + /// + /// Label of server + /// Found server or null if can not find + private MDSServer FindServer(Label label) + { + foreach (var server in _servers) + { + if (server.LabelOfServer == label) + { + return server; + } + } + + return null; + } + + /// + /// Creates a label for a server. + /// + /// Server + /// Label + private Label CreateServerLabel(MDSServer server) + { + var label = new Label + { + BackColor = Color.DodgerBlue, + BorderStyle = BorderStyle.FixedSingle, + Font = new Font("Verdana", 9.75F, FontStyle.Bold, GraphicsUnit.Point, 162), + ForeColor = Color.White, + Location = new Point(1, 1), + Name = "lbl" + server.ServerInfo.Name, + Size = new Size(180, 40), + TabIndex = 1, + Text = server.ServerInfo.Name + Environment.NewLine + server.ServerInfo.IpAddress, + TextAlign = ContentAlignment.MiddleCenter + }; + + if (server == _thisServer) + { + label.BackColor = Color.Crimson; + } + + label.ContextMenuStrip = cmsServerRightMenu; + label.MouseDown += ServerLabel_MouseDown; + label.MouseUp += ServerLabel_MouseUp; + label.MouseMove += ServerLabel_MouseMove; + label.MouseDoubleClick += ServerLabel_MouseDoubleClick; + + pnlDesign.Controls.Add(label); + + return label; + } + + /// + /// Searches all labels if any label's left value is 5 pixel close to given x value. + /// + /// x value to search + /// int.MinValue: No label found, >int.MinValue: Left value of found label + private int FindGuideLabelX(int x) + { + foreach (var mdsServer in _servers) + { + if (mdsServer.LabelOfServer == _selectedLabel) + { + continue; + } + + if (Math.Abs(mdsServer.LabelOfServer.Left - x) < 6) + { + return mdsServer.LabelOfServer.Left; + } + } + + return int.MinValue; + } + + /// + /// Searches all labels if any label's Top value is 5 pixel close to given y value. + /// + /// y value to search + /// int.MinValue: No label found, >int.MinValue: Top value of found label + private int FindGuideLabelY(int y) + { + foreach (var mdsServer in _servers) + { + if (mdsServer.LabelOfServer == _selectedLabel) + { + continue; + } + + if (Math.Abs(mdsServer.LabelOfServer.Top - y) < 6) + { + return mdsServer.LabelOfServer.Top; + } + } + + return int.MinValue; + } + + /// + /// Links two servers if they are not linked, else unlink them. + /// + /// Label of first server + /// Label of second server + private void LinkOrUnlinkServers(Label firstServerLabel, Label secondServerLabel) + { + if (firstServerLabel == secondServerLabel) + { + return; + } + + var firstServer = FindServer(firstServerLabel); + var secondServer = FindServer(secondServerLabel); + if (firstServer == null || secondServer == null) + { + return; + } + + if (firstServer.Adjacents.Contains(secondServer)) + { + firstServer.Adjacents.Remove(secondServer); + if (secondServer.Adjacents.Contains(firstServer)) + { + secondServer.Adjacents.Remove(firstServer); + } + } + else + { + firstServer.Adjacents.Add(secondServer); + if (!secondServer.Adjacents.Contains(firstServer)) + { + secondServer.Adjacents.Add(firstServer); + } + } + } + + /// + /// Draws all lines between adjacent servers. + /// + private void DrawAllLines(Graphics graphics) + { + if (_servers == null) + { + return; + } + + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + if (_selectedLabel != null && _movingLabelGuideLabelX >= 0) + { + graphics.DrawLine(_guideLinePen, _selectedLabel.Left, 0, _selectedLabel.Left, pnlDesign.Height); + graphics.DrawLine(_guideLinePen, _selectedLabel.Right, 0, _selectedLabel.Right, pnlDesign.Height); + } + + if (_selectedLabel != null && _movingLabelGuideLabelY >= 0) + { + graphics.DrawLine(_guideLinePen, 0, _selectedLabel.Top, pnlDesign.Width, _selectedLabel.Top); + graphics.DrawLine(_guideLinePen, 0, _selectedLabel.Bottom, pnlDesign.Width, _selectedLabel.Bottom); + } + + if (_selectedLabel != null) + { + graphics.DrawRectangle( + _labelSelectingPen, + _selectedLabel.Left - 4, + _selectedLabel.Top - 4, + _selectedLabel.Width + 7, + _selectedLabel.Height + 7 + ); + } + + var passedServers = new List(); + foreach (var server in _servers) + { + passedServers.Add(server); + foreach (var adjacent in server.Adjacents) + { + if (passedServers.Contains(adjacent)) + { + continue; + } + + graphics.DrawLine( + _linePen, + server.LabelOfServer.Location.X + server.LabelOfServer.Width / 2, + server.LabelOfServer.Location.Y + server.LabelOfServer.Height / 2, + adjacent.LabelOfServer.Location.X + adjacent.LabelOfServer.Width / 2, + adjacent.LabelOfServer.Location.Y + adjacent.LabelOfServer.Height / 2 + ); + } + } + } + + /// + /// Finds selected server on design area. + /// + /// Selected server or null if not selected any server + private MDSServer GetSelectedServer() + { + return (_selectedLabel == null) ? null : FindServer(_selectedLabel); + } + + #endregion + + #region Sub classes + + /// + /// Represents a MDS server on design area. + /// + private class MDSServer + { + /// + /// Reference to ServerGraphInfo.ServerOnGraph associated with this MDSServer object. + /// + public ServerGraphInfo.ServerOnGraph ServerInfo { get; set; } + + /// + /// List of adjacent servers of MDS server. + /// + public List Adjacents { get; private set; } + + /// + /// Label that represents server on ServerGraphForm form. + /// + public Label LabelOfServer { get; set; } + + /// + /// Creates a new MDSServer object. + /// + public MDSServer() + { + Adjacents = new List(); + } + } + + #endregion + } +} diff --git a/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.resx b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.resx new file mode 100644 index 0000000..99e4175 --- /dev/null +++ b/src/MDSManager/Management/GUI/MDSServers/ServerGraphForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 185, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/MainForm.Designer.cs b/src/MDSManager/Management/GUI/MainForm.Designer.cs new file mode 100644 index 0000000..45d1dff --- /dev/null +++ b/src/MDSManager/Management/GUI/MainForm.Designer.cs @@ -0,0 +1,122 @@ +namespace MDS.Management.GUI +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainMenu = new System.Windows.Forms.MenuStrip(); + this.mdsServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.serverGraphToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.routesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.clientApplicationsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.applicationListToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mainMenu.SuspendLayout(); + this.SuspendLayout(); + // + // mainMenu + // + this.mainMenu.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.mainMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mdsServersToolStripMenuItem, + this.clientApplicationsToolStripMenuItem}); + this.mainMenu.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.HorizontalStackWithOverflow; + this.mainMenu.Location = new System.Drawing.Point(0, 0); + this.mainMenu.Name = "mainMenu"; + this.mainMenu.Size = new System.Drawing.Size(813, 24); + this.mainMenu.TabIndex = 1; + this.mainMenu.Text = "Main Menu"; + // + // mdsServersToolStripMenuItem + // + this.mdsServersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.serverGraphToolStripMenuItem, + this.routesToolStripMenuItem}); + this.mdsServersToolStripMenuItem.Name = "mdsServersToolStripMenuItem"; + this.mdsServersToolStripMenuItem.Size = new System.Drawing.Size(87, 20); + this.mdsServersToolStripMenuItem.Text = "MDS Servers"; + // + // serverGraphToolStripMenuItem + // + this.serverGraphToolStripMenuItem.Name = "serverGraphToolStripMenuItem"; + this.serverGraphToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.serverGraphToolStripMenuItem.Text = "Server Graph"; + this.serverGraphToolStripMenuItem.Click += new System.EventHandler(this.ServerGraphToolStripMenuItem_Click); + // + // routesToolStripMenuItem + // + this.routesToolStripMenuItem.Name = "routesToolStripMenuItem"; + this.routesToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.routesToolStripMenuItem.Text = "Routes"; + this.routesToolStripMenuItem.Visible = false; + this.routesToolStripMenuItem.Click += new System.EventHandler(this.RoutesToolStripMenuItem_Click); + // + // clientApplicationsToolStripMenuItem + // + this.clientApplicationsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.applicationListToolStripMenuItem}); + this.clientApplicationsToolStripMenuItem.Name = "clientApplicationsToolStripMenuItem"; + this.clientApplicationsToolStripMenuItem.Size = new System.Drawing.Size(117, 20); + this.clientApplicationsToolStripMenuItem.Text = "Client Applications"; + // + // applicationListToolStripMenuItem + // + this.applicationListToolStripMenuItem.Name = "applicationListToolStripMenuItem"; + this.applicationListToolStripMenuItem.Size = new System.Drawing.Size(155, 22); + this.applicationListToolStripMenuItem.Text = "Application List"; + this.applicationListToolStripMenuItem.Click += new System.EventHandler(this.ApplicationListToolStripMenuItem_Click); + // + // MainForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(813, 464); + this.Controls.Add(this.mainMenu); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.IsMdiContainer = true; + this.MainMenuStrip = this.mainMenu; + this.Name = "MainForm"; + this.Text = "MDS Management GUI"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.Load += new System.EventHandler(this.MainForm_Load); + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); + this.mainMenu.ResumeLayout(false); + this.mainMenu.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip mainMenu; + private System.Windows.Forms.ToolStripMenuItem mdsServersToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem clientApplicationsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem applicationListToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem serverGraphToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem routesToolStripMenuItem; + } +} + diff --git a/src/MDSManager/Management/GUI/MainForm.cs b/src/MDSManager/Management/GUI/MainForm.cs new file mode 100644 index 0000000..1edb169 --- /dev/null +++ b/src/MDSManager/Management/GUI/MainForm.cs @@ -0,0 +1,239 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Reflection; +using System.Windows.Forms; +using log4net; +using MDS.Communication.Messages.ControllerMessages; +using MDS.Exceptions; +using MDS.GUI; +using MDS.Management.GUI.ClientApplications; +using MDS.Management.GUI.MDSServers; + +namespace MDS.Management.GUI +{ + /// + /// This is the main form of the application. + /// + public partial class MainForm : Form + { + #region Private fields + + /// + /// Reference to logger. + /// + private static readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// All communication with MDS server is performed using this object. + /// + private readonly MDSController _controller; + + #region References to the open forms + + private ServerGraphForm _serverGraphForm; + + private ApplicationListForm _applicationListForm; + + private RoutesForm _routesForm; + + #endregion + + #endregion + + #region Constructors + + /// + /// Default empty constructor. + /// + public MainForm(MDSController controller) + { + InitializeComponent(); + _controller = controller; + _controller.ControlMessageReceived += Controller_ControlMessageReceived; + } + + #endregion + + #region Private methods + + #region Sub form open/close + + #region ApplicationListForm + + private void ApplicationListToolStripMenuItem_Click(object sender, EventArgs e) + { + if(_applicationListForm == null) + { + var form = new ApplicationListForm(_controller) {MdiParent = this}; + form.FormClosed += ApplicationList_FormClosed; + form.Show(); + _applicationListForm = form; + } + else + { + _applicationListForm.Activate(); + } + } + + private void ApplicationList_FormClosed(object sender, FormClosedEventArgs e) + { + _applicationListForm = null; + } + + #endregion + + #region ServerGraphForm + + private void ServerGraphToolStripMenuItem_Click(object sender, EventArgs e) + { + if (_serverGraphForm == null) + { + _serverGraphForm = new ServerGraphForm(_controller) { MdiParent = this }; + _serverGraphForm.FormClosed += ServerGraphForm_FormClosed; + _serverGraphForm.Show(); + } + else + { + _serverGraphForm.Activate(); + } + } + + private void ServerGraphForm_FormClosed(object sender, FormClosedEventArgs e) + { + _serverGraphForm = null; + } + + #endregion + + #region RoutesForm + + private void RoutesToolStripMenuItem_Click(object sender, EventArgs e) + { + if (_routesForm == null) + { + _routesForm = new RoutesForm() { MdiParent = this }; + _routesForm.FormClosed += RoutesForm_FormClosed; + _routesForm.Show(); + } + else + { + _routesForm.Activate(); + } + } + + private void RoutesForm_FormClosed(object sender, FormClosedEventArgs e) + { + _routesForm = null; + } + + #endregion + + #endregion + + #region MainForm event handlers + + private void MainForm_Load(object sender, EventArgs e) + { + try + { + _controller.Connect(); + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + MDSGuiHelper.ShowErrorMessage("Can not connected to MDS Server. Detail: " + ex.Message); + Close(); + } + } + + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + try + { + _controller.Disconnect(); + } + catch (Exception ex) + { + Logger.Warn(ex.Message, ex); + } + } + + #endregion + + #region Incoming message handling and processing + + /// + /// This method handles ControlMessageReceived event of _controller object. + /// It calls appropriate method to process message according to message's type. + /// + /// Sender of event + /// Event arguments + private void Controller_ControlMessageReceived(object sender, ControlMessageReceivedEventArgs e) + { + try + { + switch (e.Message.MessageTypeId) + { + case ControlMessageFactory.MessageTypeIdClientApplicationRefreshEventMessage: + ProcessClientApplicationRefreshEventMessage(e.Message as ClientApplicationRefreshEventMessage); + break; + case ControlMessageFactory.MessageTypeIdClientApplicationRemovedEventMessage: + ProcessClientApplicationRemovedEventMessage(e.Message as ClientApplicationRemovedEventMessage); + break; + default: + throw new MDSException("Undefined MessageTypeId for ControlMessage: " + e.Message.MessageTypeId); + } + } + catch (Exception ex) + { + Logger.Error(ex.Message, ex); + } + } + + /// + /// Processes a ClientApplicationRefreshEventMessage message. + /// + /// Message to process + private void ProcessClientApplicationRefreshEventMessage(ClientApplicationRefreshEventMessage message) + { + if (_applicationListForm != null) + { + _applicationListForm.GetClientApplicationRefreshEventMessage(message); + } + } + + /// + /// Processes a ClientApplicationRemovedEventMessage message. + /// + /// Message to process + private void ProcessClientApplicationRemovedEventMessage(ClientApplicationRemovedEventMessage message) + { + if (_applicationListForm != null) + { + _applicationListForm.GetClientApplicationRemovedEventMessage(message); + } + } + + #endregion + + #endregion + } +} diff --git a/src/MDSManager/Management/GUI/MainForm.resx b/src/MDSManager/Management/GUI/MainForm.resx new file mode 100644 index 0000000..d44d40e --- /dev/null +++ b/src/MDSManager/Management/GUI/MainForm.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/src/MDSManager/Management/GUI/Program.cs b/src/MDSManager/Management/GUI/Program.cs new file mode 100644 index 0000000..5fb4064 --- /dev/null +++ b/src/MDSManager/Management/GUI/Program.cs @@ -0,0 +1,44 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Windows.Forms; + +namespace MDS.Management.GUI +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + var connectToServerForm = new ConnectToServerForm(); + Application.Run(connectToServerForm); + if (connectToServerForm.MDSController != null) + { + Application.Run(new MainForm(connectToServerForm.MDSController)); + } + } + } +} diff --git a/src/MDSManager/Management/GUI/Tools/DoubleBufferedPanel.cs b/src/MDSManager/Management/GUI/Tools/DoubleBufferedPanel.cs new file mode 100644 index 0000000..b6bda78 --- /dev/null +++ b/src/MDSManager/Management/GUI/Tools/DoubleBufferedPanel.cs @@ -0,0 +1,41 @@ +/* +DotNetMQ - A Complete Message Broker For .NET +Copyright (C) 2011 Halil ibrahim KALKAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace MDS.Management.GUI.Tools +{ + /// + /// This class is created to add Double Buffer capabilities to a Panel. + /// + public class DoubleBufferedPanel : Panel + { + public DoubleBufferedPanel() + { + SetStyle( + ControlStyles.UserPaint | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.OptimizedDoubleBuffer, true); + } + } +} diff --git a/src/MDSManager/Properties/AssemblyInfo.cs b/src/MDSManager/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d755a4b --- /dev/null +++ b/src/MDSManager/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MDSManager")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("SESTEK A.Ş.")] +[assembly: AssemblyProduct("MDSManager")] +[assembly: AssemblyCopyright("Copyright © SESTEK A.Ş. 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: log4net.Config.XmlConfigurator(Watch = true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f1799c51-8af6-4c69-8b07-2ab903867173")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.0.0")] +[assembly: AssemblyFileVersion("0.9.0.0")] diff --git a/src/MDSManager/Properties/Resources.Designer.cs b/src/MDSManager/Properties/Resources.Designer.cs new file mode 100644 index 0000000..f3296d2 --- /dev/null +++ b/src/MDSManager/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3607 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MDS.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MDS.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/src/MDSManager/Properties/Resources.resx b/src/MDSManager/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/src/MDSManager/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/MDSManager/Properties/Settings.Designer.cs b/src/MDSManager/Properties/Settings.Designer.cs new file mode 100644 index 0000000..9e59d20 --- /dev/null +++ b/src/MDSManager/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.3607 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MDS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/src/MDSManager/Properties/Settings.settings b/src/MDSManager/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/src/MDSManager/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Tests/ServerTest/App.config b/src/Tests/ServerTest/App.config new file mode 100644 index 0000000..c5fd44f --- /dev/null +++ b/src/Tests/ServerTest/App.config @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/ServerTest/MDSSettings.design.xml b/src/Tests/ServerTest/MDSSettings.design.xml new file mode 100644 index 0000000..0f978c4 --- /dev/null +++ b/src/Tests/ServerTest/MDSSettings.design.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Tests/ServerTest/MDSSettings.xml b/src/Tests/ServerTest/MDSSettings.xml new file mode 100644 index 0000000..3b316df --- /dev/null +++ b/src/Tests/ServerTest/MDSSettings.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/ServerTest/Program.cs b/src/Tests/ServerTest/Program.cs new file mode 100644 index 0000000..9c52a9b --- /dev/null +++ b/src/Tests/ServerTest/Program.cs @@ -0,0 +1,28 @@ +using System; +using MDS; + +namespace ServerTest +{ + class Program + { + static void Main(string[] args) + { + try + { + var server = new MDSServer(); + server.Start(); + + Console.WriteLine("DotNetMQ server has started."); + Console.WriteLine("Press enter to stop..."); + Console.ReadLine(); + + server.Stop(true); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + Console.ReadLine(); + } + } + } +} diff --git a/src/Tests/ServerTest/Properties/AssemblyInfo.cs b/src/Tests/ServerTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..23158b3 --- /dev/null +++ b/src/Tests/ServerTest/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ServerTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("ServerTest")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: log4net.Config.XmlConfigurator(Watch = true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0f866d71-29fa-4e4d-a7d6-896adcd9c33d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Tests/ServerTest/ServerTest.csproj b/src/Tests/ServerTest/ServerTest.csproj new file mode 100644 index 0000000..82c842f --- /dev/null +++ b/src/Tests/ServerTest/ServerTest.csproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4B4B3551-78B8-4CE2-BD0A-98E8EC496E80} + Exe + Properties + ServerTest + ServerTest + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\Dependencies\Libraries\log4net.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {055149D1-A267-4E2E-B8AE-EA7848A45701} + MDSCommonLib + + + {AB59B8B2-D414-483F-AA72-A2644D92C86D} + MDSCore + + + + + + Always + + + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/src/Tests/ServerTest/SqliteDB/MDS.s3db b/src/Tests/ServerTest/SqliteDB/MDS.s3db new file mode 100644 index 0000000..1a1cdce Binary files /dev/null and b/src/Tests/ServerTest/SqliteDB/MDS.s3db differ diff --git a/src/Tools/MDSServiceProxyGenerator/MDSServiceProxyGenerator.csproj b/src/Tools/MDSServiceProxyGenerator/MDSServiceProxyGenerator.csproj new file mode 100644 index 0000000..b412e16 --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/MDSServiceProxyGenerator.csproj @@ -0,0 +1,96 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {B49D18FF-DD77-46A4-880A-F0B9598C6070} + WinExe + Properties + MDS + MDSServiceProxyGenerator + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + Form + + + ProxyGeneratorForm.cs + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + ProxyGeneratorForm.cs + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {055149D1-A267-4E2E-B8AE-EA7848A45701} + MDSCommonLib + + + + + \ No newline at end of file diff --git a/src/Tools/MDSServiceProxyGenerator/Properties/AssemblyInfo.cs b/src/Tools/MDSServiceProxyGenerator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ee0952e --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MDSServiceProxyGenerator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("MDSServiceProxyGenerator")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2f34b99f-0166-4e7b-8dca-bed619f8600b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Tools/MDSServiceProxyGenerator/Properties/Resources.Designer.cs b/src/Tools/MDSServiceProxyGenerator/Properties/Resources.Designer.cs new file mode 100644 index 0000000..28a9150 --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4952 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MDS.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MDS.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/src/Tools/MDSServiceProxyGenerator/Properties/Resources.resx b/src/Tools/MDSServiceProxyGenerator/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Tools/MDSServiceProxyGenerator/Properties/Settings.Designer.cs b/src/Tools/MDSServiceProxyGenerator/Properties/Settings.Designer.cs new file mode 100644 index 0000000..0231d03 --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.4952 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace MDS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/src/Tools/MDSServiceProxyGenerator/Properties/Settings.settings b/src/Tools/MDSServiceProxyGenerator/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/Program.cs b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/Program.cs new file mode 100644 index 0000000..10f20e1 --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace MDS.Tools.ProxyGenerator +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new ProxyGeneratorForm()); + } + } +} \ No newline at end of file diff --git a/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.Designer.cs b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.Designer.cs new file mode 100644 index 0000000..03721cc --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.Designer.cs @@ -0,0 +1,269 @@ +namespace MDS.Tools.ProxyGenerator +{ + partial class ProxyGeneratorForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.mainPanel = new System.Windows.Forms.Panel(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.btnTargetFolderBrowse = new System.Windows.Forms.Button(); + this.txtTargetFolder = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtNamespace = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnGenerateCode = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.cmbClasses = new System.Windows.Forms.ComboBox(); + this.btnBrowseAssembly = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.txtAssemblyPath = new System.Windows.Forms.TextBox(); + this.lblApplicationListHeader = new System.Windows.Forms.Label(); + this.AssemblyBrowseDialog = new System.Windows.Forms.OpenFileDialog(); + this.TargetFolderBrowseDialog = new System.Windows.Forms.FolderBrowserDialog(); + this.mainPanel.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // mainPanel + // + this.mainPanel.BackColor = System.Drawing.Color.Lavender; + this.mainPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.mainPanel.Controls.Add(this.groupBox2); + this.mainPanel.Controls.Add(this.btnGenerateCode); + this.mainPanel.Controls.Add(this.groupBox1); + this.mainPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.mainPanel.Location = new System.Drawing.Point(0, 0); + this.mainPanel.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.mainPanel.Name = "mainPanel"; + this.mainPanel.Size = new System.Drawing.Size(844, 212); + this.mainPanel.TabIndex = 3; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.btnTargetFolderBrowse); + this.groupBox2.Controls.Add(this.txtTargetFolder); + this.groupBox2.Controls.Add(this.label3); + this.groupBox2.Controls.Add(this.txtNamespace); + this.groupBox2.Controls.Add(this.label1); + this.groupBox2.Location = new System.Drawing.Point(8, 90); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(823, 78); + this.groupBox2.TabIndex = 15; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Proxy Class Generation"; + // + // btnTargetFolderBrowse + // + this.btnTargetFolderBrowse.BackColor = System.Drawing.Color.DodgerBlue; + this.btnTargetFolderBrowse.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnTargetFolderBrowse.ForeColor = System.Drawing.Color.White; + this.btnTargetFolderBrowse.Location = new System.Drawing.Point(668, 45); + this.btnTargetFolderBrowse.Name = "btnTargetFolderBrowse"; + this.btnTargetFolderBrowse.Size = new System.Drawing.Size(149, 28); + this.btnTargetFolderBrowse.TabIndex = 17; + this.btnTargetFolderBrowse.Text = "Browse..."; + this.btnTargetFolderBrowse.UseVisualStyleBackColor = false; + this.btnTargetFolderBrowse.Click += new System.EventHandler(this.btnTargetFolderBrowse_Click); + // + // txtTargetFolder + // + this.txtTargetFolder.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtTargetFolder.Location = new System.Drawing.Point(158, 48); + this.txtTargetFolder.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtTargetFolder.MaxLength = 50; + this.txtTargetFolder.Name = "txtTargetFolder"; + this.txtTargetFolder.Size = new System.Drawing.Size(504, 23); + this.txtTargetFolder.TabIndex = 16; + // + // label3 + // + this.label3.BackColor = System.Drawing.Color.LightSlateGray; + this.label3.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label3.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label3.ForeColor = System.Drawing.Color.White; + this.label3.Location = new System.Drawing.Point(6, 48); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(150, 23); + this.label3.TabIndex = 15; + this.label3.Text = "Target Folder:"; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtNamespace + // + this.txtNamespace.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtNamespace.Location = new System.Drawing.Point(158, 23); + this.txtNamespace.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtNamespace.MaxLength = 50; + this.txtNamespace.Name = "txtNamespace"; + this.txtNamespace.Size = new System.Drawing.Size(504, 23); + this.txtNamespace.TabIndex = 14; + // + // label1 + // + this.label1.BackColor = System.Drawing.Color.LightSlateGray; + this.label1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label1.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label1.ForeColor = System.Drawing.Color.White; + this.label1.Location = new System.Drawing.Point(6, 23); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(150, 23); + this.label1.TabIndex = 13; + this.label1.Text = "Namespace:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // btnGenerateCode + // + this.btnGenerateCode.BackColor = System.Drawing.Color.ForestGreen; + this.btnGenerateCode.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnGenerateCode.ForeColor = System.Drawing.Color.White; + this.btnGenerateCode.Location = new System.Drawing.Point(461, 174); + this.btnGenerateCode.Name = "btnGenerateCode"; + this.btnGenerateCode.Size = new System.Drawing.Size(209, 28); + this.btnGenerateCode.TabIndex = 4; + this.btnGenerateCode.Text = "Generate Code"; + this.btnGenerateCode.UseVisualStyleBackColor = false; + this.btnGenerateCode.Click += new System.EventHandler(this.btnGenerateCode_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.cmbClasses); + this.groupBox1.Controls.Add(this.btnBrowseAssembly); + this.groupBox1.Controls.Add(this.label2); + this.groupBox1.Controls.Add(this.txtAssemblyPath); + this.groupBox1.Controls.Add(this.lblApplicationListHeader); + this.groupBox1.Location = new System.Drawing.Point(8, 6); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(823, 78); + this.groupBox1.TabIndex = 6; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Service Class Selection"; + // + // cmbClasses + // + this.cmbClasses.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmbClasses.FormattingEnabled = true; + this.cmbClasses.Location = new System.Drawing.Point(158, 47); + this.cmbClasses.Name = "cmbClasses"; + this.cmbClasses.Size = new System.Drawing.Size(504, 24); + this.cmbClasses.TabIndex = 12; + // + // btnBrowseAssembly + // + this.btnBrowseAssembly.BackColor = System.Drawing.Color.DodgerBlue; + this.btnBrowseAssembly.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.btnBrowseAssembly.ForeColor = System.Drawing.Color.White; + this.btnBrowseAssembly.Location = new System.Drawing.Point(668, 19); + this.btnBrowseAssembly.Name = "btnBrowseAssembly"; + this.btnBrowseAssembly.Size = new System.Drawing.Size(149, 28); + this.btnBrowseAssembly.TabIndex = 7; + this.btnBrowseAssembly.Text = "Browse..."; + this.btnBrowseAssembly.UseVisualStyleBackColor = false; + this.btnBrowseAssembly.Click += new System.EventHandler(this.btnBrowseAssembly_Click); + // + // label2 + // + this.label2.BackColor = System.Drawing.Color.LightSlateGray; + this.label2.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.label2.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.label2.ForeColor = System.Drawing.Color.White; + this.label2.Location = new System.Drawing.Point(6, 22); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(150, 23); + this.label2.TabIndex = 11; + this.label2.Text = "Assembly:"; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // txtAssemblyPath + // + this.txtAssemblyPath.BackColor = System.Drawing.Color.WhiteSmoke; + this.txtAssemblyPath.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtAssemblyPath.Location = new System.Drawing.Point(158, 22); + this.txtAssemblyPath.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); + this.txtAssemblyPath.MaxLength = 50; + this.txtAssemblyPath.Name = "txtAssemblyPath"; + this.txtAssemblyPath.ReadOnly = true; + this.txtAssemblyPath.Size = new System.Drawing.Size(504, 23); + this.txtAssemblyPath.TabIndex = 1; + // + // lblApplicationListHeader + // + this.lblApplicationListHeader.BackColor = System.Drawing.Color.LightSlateGray; + this.lblApplicationListHeader.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblApplicationListHeader.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblApplicationListHeader.ForeColor = System.Drawing.Color.White; + this.lblApplicationListHeader.Location = new System.Drawing.Point(6, 47); + this.lblApplicationListHeader.Name = "lblApplicationListHeader"; + this.lblApplicationListHeader.Size = new System.Drawing.Size(150, 24); + this.lblApplicationListHeader.TabIndex = 2; + this.lblApplicationListHeader.Text = "Class:"; + this.lblApplicationListHeader.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // AssemblyBrowseDialog + // + this.AssemblyBrowseDialog.FileName = "openFileDialog1"; + this.AssemblyBrowseDialog.Filter = "Executable files|*.exe|Dll files|*.dll|All files|*.*"; + // + // ProxyGeneratorForm + // + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; + this.ClientSize = new System.Drawing.Size(844, 212); + this.Controls.Add(this.mainPanel); + this.Font = new System.Drawing.Font("Verdana", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Margin = new System.Windows.Forms.Padding(4); + this.Name = "ProxyGeneratorForm"; + this.Text = "MDS Service Proxy Generator"; + this.mainPanel.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel mainPanel; + private System.Windows.Forms.Button btnGenerateCode; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox txtAssemblyPath; + private System.Windows.Forms.Label lblApplicationListHeader; + private System.Windows.Forms.Button btnBrowseAssembly; + private System.Windows.Forms.ComboBox cmbClasses; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox txtNamespace; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TextBox txtTargetFolder; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button btnTargetFolderBrowse; + private System.Windows.Forms.OpenFileDialog AssemblyBrowseDialog; + private System.Windows.Forms.FolderBrowserDialog TargetFolderBrowseDialog; + } +} \ No newline at end of file diff --git a/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.cs b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.cs new file mode 100644 index 0000000..bf2b25f --- /dev/null +++ b/src/Tools/MDSServiceProxyGenerator/Tools/ProxyGenerator/ProxyGeneratorForm.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; +using MDS.Client.MDSServices; +using MDS.GUI; + +namespace MDS.Tools.ProxyGenerator +{ + public partial class ProxyGeneratorForm : Form + { + private Assembly _selectedAssembly; + + public ProxyGeneratorForm() + { + InitializeComponent(); + } + + private void btnBrowseAssembly_Click(object sender, EventArgs e) + { + var dialogResult = AssemblyBrowseDialog.ShowDialog(); + if (dialogResult != DialogResult.OK) + { + return; + } + + txtAssemblyPath.Text = AssemblyBrowseDialog.FileName; + + try + { + FillClasses(); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + + private void FillClasses() + { + cmbClasses.Items.Clear(); + cmbClasses.Items.Add("All MDSService classes"); + var assembly = Assembly.LoadFile(txtAssemblyPath.Text); + var types = assembly.GetTypes(); + foreach (var type in types) + { + var attributes = type.GetCustomAttributes(typeof (MDSServiceAttribute), true); + if (attributes.Length <= 0) + { + continue; + } + + cmbClasses.Items.Add(type.FullName); + } + + _selectedAssembly = assembly; + cmbClasses.SelectedIndex = 0; + } + + private void btnGenerateCode_Click(object sender, EventArgs e) + { + btnGenerateCode.Enabled = false; + Application.DoEvents(); + + try + { + GenerateCode(); + MDSGuiHelper.ShowInfoDialog("Proxy classes are generated.", "Success."); + } + catch (Exception ex) + { + MDSGuiHelper.ShowErrorMessage(ex.Message); + } + finally + { + btnGenerateCode.Enabled = true; + } + } + + private void GenerateCode() + { + if(cmbClasses.Items.Count < 2) + { + MDSGuiHelper.ShowWarningMessage("There is no class to generate."); + return; + } + + var namespaceName = txtNamespace.Text; + if(string.IsNullOrEmpty(namespaceName)) + { + MDSGuiHelper.ShowWarningMessage("Please enter a namespace."); + return; + } + + var targetFolder = txtTargetFolder.Text; + if (string.IsNullOrEmpty(targetFolder)) + { + MDSGuiHelper.ShowWarningMessage("Please enter a target folder to generate code files."); + return; + } + + if (!Directory.Exists(targetFolder)) + { + Directory.CreateDirectory(targetFolder); + } + + if(cmbClasses.SelectedIndex == 0) + { + for(var i=1;i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 183, 17 + + \ No newline at end of file