Openfire practice summary and Openfire practice summary
Openfire has been studied since March. In fact, an IM system is designed for this reason. I have never thought of so many open-source products that can be used for IM, nor how powerful the XMPP protocol is. It seems that standards are the first. Good standards can promote industrial development.
The establishment and simple demo of Openfire has previously written an article titled Technical Notes: XMPP openfire + spark + smack. At that time, I was mainly concerned about how to make this system run, I am still at this stage. I just learned more and left some notes.
1. XMPP learning is very important.
At first, I thought it was too easy to set up an Openfire + spark, and the spark interface could be changed to a new product. At that time, I thought that XMPP protocol was not too deep. Only when simple things are over can we find that the most important thing is the protocol itself. Only by understanding the protocol can we better understand the operation of the system and how complicated the system is. Of course, it is a bit complicated for me, especially when it comes to the combination of front and back ends for design and start.
For this reason, I recommend a Chinese XMPP translation website: http://wiki.jabbercn.org/%e9%a6%96%e9%a1%b5.
Of course, if the English is good, then the original version: http://xmpp.org/about/technology-overview.html
After a period of study, I feel that QQ is really similar to XMPP in terms of basic principles, but the Protocol format used is somewhat different. Maybe this is the abstract level of instant messaging. But is it a waste of traffic to use the XML markup language? Although XMPP is very convenient to scale up, these labels are really big enough. For text chats, the traffic generated by the Intermediate mark may be equivalent to the chat content. After all, I have not yet reached this stage of consideration for large traffic, so this is just an idea.
2. Some design points and ideas of Openfire
The source code of Openfire is clear as a whole. The extension supports plug-ins and component modes. In recent extensions, we found that the source code of openfire is not very well modified and highly dependent, but the dependencies between modules are loose, and class dependencies in modules are closely coupled. However, Openfire can be extended through plug-ins, and the dependency on the source code itself is much smaller, so it is quite good in general.
The plug-in extension methods in Openfire are as follows:
In XMPP, the IQ Package refers to information/query, which can be used for data query between the server and the client. Openfir implements an IQRouter to process the IQ package. IQHandler is the actual IQ Package Processing Unit. IQHandler intercepts and processes buckets Based on namespace. You can customize your own namespace.
IQHandler provides two Abstract methods for implementation of derived classes:
/** * Handles the received IQ packet. * * @param packet the IQ packet to handle. * @return the response to send back. * @throws UnauthorizedException if the user that sent the packet is not * authorized to request the given operation. */ public abstract IQ handleIQ(IQ packet) throws UnauthorizedException; /** * Returns the handler information to help generically handle IQ packets. * IQHandlers that aren't local server iq handlers (e.g. chatbots, transports, etc) * return <tt>null</tt>. * * @return The IQHandlerInfo for this handler */ public abstract IQHandlerInfo getInfo();
HandleIQThe solution is to unpack the package, process the service, and finally return the result package.
GetInfoIs the command space used to return the current IQHandler
To make the handler take effect, you also need to register it with IQRouter. You can create an IQHandler object and add it at plug-in startup:
@Override public void initializePlugin(PluginManager manager, File pluginDirectory) { iqHandler = new IQGroupChatHander(); iqRouter = XMPPServer.getInstance().getIQRouter(); iqRouter.addHandler(iqHandler); } @Override public void destroyPlugin() { iqRouter.removeHandler(iqHandler); }
Compoent is also a common extension method that can be used to process packages in a specific subdomain. For example, MUC registers different services and each Service has a subdomain. The system will distribute data packets from different subdomains to specialized services for processing. This brings about the benefit of completely customizing a set of sub-domain package processing services. The public account subscription number will be implemented in the future. Openfire also has a remote component mechanism that can be extended into an independent business system, so that openfire can act only as the core of message processing.
The specific application is also relatively simple. It implements the Component Interface and registers it to ComponentManager. The most important method in the Component interface is the processPacket method. The Code is as follows:
/** * Processes a packet sent to this Component. * * @param packet the packet. * @see ComponentManager#sendPacket(Component, Packet) */ public void processPacket(Packet packet);
Note that the parameter in the method is Packet, which indicates that the communication primitives under all subdomains can be used for processing here.
The registration and exit methods are as follows:
componentManager = ComponentManagerFactory.getComponentManager(); try { componentManager.addComponent("subdomain", this); } catch (Exception e) { Log.error(e.getMessage(), e); System.err.println(e); } try { componentManager.removeComponent("subdomain"); } catch (ComponentException e) { Log.error(e.getMessage(), e); }
In Openfire, the transmission is based on packet, and different communication primitives such as message, roster, JID, and IQ are generated on packet. Based on this principle, you only need to provide a packet interception mechanism to implement a powerful expansion mechanism. This mechanism is provided in Openfire. The packet blocker is provided in IQRouter \ PresenceRouter \ MessageRouter.
// Invoke the interceptors before we process the read packet InterceptorManager.getInstance().invokeInterceptors(packet, session, true, false);
The Interceptor is registered in InterceptorManager and will call the interceptor when handling the package. The above code is an example of the Code intercepted in the route.
Implement the PacketInterceptor interface and register it with InterceptorManager.
InterceptorManager.getInstance().addInterceptor(interceptor);InterceptorManager.getInstance().removeInterceptor(interceptor);
With the above three methods, Openfire can develop a variety of usage, so the official has also implemented multiple plug-ins for use. We recommend that you use plug-ins for openfire extensions unless you have high customization requirements.
My requirements can basically be met, and it will be very easy to upgrade the new version in the future without any problems.
3. Spark tangle
Spark also comes from jivesoftware, but it doesn't feel as good as it is on expansion. Maybe I didn't fully understand its extension principle. In fact, I need to rewrite the Spark UI and add my own functions, such as group and subscription number. At first, I thought that Spark also supports plug-ins. However, when I finally changed the code, I found that the dependency in it was too deep. Basically, there were dependencies related to the interface. In the end, I may have to rewrite one set.
In fact, there is a UIComponentRegistry class in Spark, and some major interfaces are registered in this class. However, most of these registered classes cannot be derived from new classes to replace these registered classes. For example
private static Class<? extends ChatRoom> chatRoomClass = ChatRoomImpl.class;
This is the registration class in the chat window. If I want to write my own chat window, can I directly replace this registration class? No, because it will be used in all other codes.
@Override public void filesDropped(Collection<File> files, Component component) { if (component instanceof ChatRoomImpl) { ChatRoomImpl roomImpl = (ChatRoomImpl) component; for (File file : files) { SparkManager.getTransferManager().sendFile(file, roomImpl.getParticipantJID()); } SparkManager.getChatManager().getChatContainer() .activateChatRoom(roomImpl); } }
In another class, you can directly use the derived class for type determination. This is just a vertex, and there are too many similar vertices. Therefore, it is unlikely that the expected goal is achieved by deriving the Class Registered in UIComponentRegistry. In addition, due to limited time, I am too lazy to care about this, so that the development can be directly changed on the source code.
The worst thing is that the modern code is greatly changed during version 2.7.7 upgrade. This version is upgraded to version smack4.x and a large number of new features of version 1.8 are used. So it was upgraded after some code merging. In addition, smack basically does not provide extensions, but only provides event subscriptions.
Spark is only cross-platform. It is easy to run on mac, and the code is java. You do not want to discard it for the moment. If you want to rewrite it in the future, consider it.