1 /*
2  * Copyright (C) 2007-2009 Jive Software and Ignite Realtime Community 2021. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.jivesoftware.openfire.session;
18 
19 import org.jivesoftware.util.TaskEngine;
20 import org.jivesoftware.util.cache.ClusterTask;
21 import org.jivesoftware.util.cache.ExternalizableUtil;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24 
25 import java.io.IOException;
26 import java.io.ObjectInput;
27 import java.io.ObjectOutput;
28 import java.net.UnknownHostException;
29 import java.util.concurrent.Future;
30 import java.util.concurrent.TimeUnit;
31 
32 /**
33  * Operations to be executed in a remote session hosted in a remote cluster node.
34  *
35  * @author Gaston Dombiak
36  */
37 public abstract class RemoteSessionTask implements ClusterTask<Object> {
38     private static final Logger Log = LoggerFactory.getLogger(RemoteSessionTask.class);
39 
40     protected Object result;
41     protected Operation operation;
42 
RemoteSessionTask()43     public RemoteSessionTask() {
44     }
45 
RemoteSessionTask(Operation operation)46     protected RemoteSessionTask(Operation operation) {
47         this.operation = operation;
48     }
49 
getSession()50     abstract Session getSession();
51 
getResult()52     public Object getResult() {
53         return result;
54     }
55 
run()56     public void run() {
57         if (operation == Operation.getStreamID) {
58             result = getSession().getStreamID();
59         }
60         else if (operation == Operation.getServerName) {
61             result = getSession().getServerName();
62         }
63         else if (operation == Operation.getCreationDate) {
64             result = getSession().getCreationDate();
65         }
66         else if (operation == Operation.getLastActiveDate) {
67             result = getSession().getLastActiveDate();
68         }
69         else if (operation == Operation.getNumClientPackets) {
70             result = getSession().getNumClientPackets();
71         }
72         else if (operation == Operation.getNumServerPackets) {
73             result = getSession().getNumServerPackets();
74         }
75         else if (operation == Operation.getCipherSuiteName) {
76             result = getSession().getCipherSuiteName();
77         }
78         else if (operation == Operation.getPeerCertificates) {
79             result = getSession().getPeerCertificates();
80         }
81         else if (operation == Operation.getSoftwareVersion) {
82             result = getSession().getSoftwareVersion();
83         }
84         else if (operation == Operation.close) {
85             // Run in another thread so we avoid blocking calls (in hazelcast)
86             final Session session = getSession();
87             if (session != null) {
88                 final Future<?> future = TaskEngine.getInstance().submit( () -> {
89                     try {
90                         if (session instanceof LocalSession) {
91                             // OF-2311: If closed by another cluster node, chances are that the session needs to be closed forcibly.
92                             // Chances of the session being resumed are neglectable, while retaining the session in a detached state
93                             // causes problems (eg: IQBindHandler could have re-issued the resource to a replacement session).
94                             ((LocalSession) session).getStreamManager().formalClose();
95                         }
96                         session.close();
97                     } catch (Exception e) {
98                         Log.info("An exception was logged while closing session: {}", session, e);
99                     }
100                 });
101                 // Wait until the close operation is done or timeout is met
102                 try {
103                     future.get(15, TimeUnit.SECONDS);
104                 }
105                 catch (Exception e) {
106                     Log.info("An exception was logged while executing RemoteSessionTask to close session: {}", session, e);
107                 }
108             }
109         }
110         else if (operation == Operation.isClosed) {
111             result = getSession().isClosed();
112         }
113         else if (operation == Operation.isSecure) {
114             result = getSession().isSecure();
115         }
116         else if (operation == Operation.getHostAddress) {
117             try {
118                 result = getSession().getHostAddress();
119             } catch (UnknownHostException e) {
120                 Log.error("Error getting address of session: " + getSession(), e);
121             }
122         }
123         else if (operation == Operation.getHostName) {
124             try {
125                 result = getSession().getHostName();
126             } catch (UnknownHostException e) {
127                 Log.error("Error getting address of session: " + getSession(), e);
128             }
129         }
130         else if (operation == Operation.validate) {
131             result = getSession().validate();
132         }
133     }
134 
writeExternal(ObjectOutput out)135     public void writeExternal(ObjectOutput out) throws IOException {
136         ExternalizableUtil.getInstance().writeBoolean(out, operation != null);
137         if (operation != null) {
138             ExternalizableUtil.getInstance().writeInt(out, operation.ordinal());
139         }
140     }
141 
readExternal(ObjectInput in)142     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
143         if (ExternalizableUtil.getInstance().readBoolean(in)) {
144             operation = Operation.values()[ExternalizableUtil.getInstance().readInt(in)];
145         }
146     }
147 
148     public enum Operation {
149         /**
150          * Basic session operations
151          */
152         getStreamID,
153         getServerName,
154         getCreationDate,
155         getLastActiveDate,
156         getNumClientPackets,
157         getNumServerPackets,
158         getCipherSuiteName,
159         getPeerCertificates,
160         getSoftwareVersion,
161         close,
162         isClosed,
163         isSecure,
164         getHostAddress,
165         getHostName,
166         validate,
167 
168         /**
169          * Operations of c2s sessions
170          */
171         isInitialized,
172         incrementConflictCount,
173         hasRequestedBlocklist,
174 
175         /**
176          * Operations of outgoing server sessions
177          */
178         getOutgoingDomainPairs,
179         isUsingServerDialback,
180 
181         /**
182          * Operations of external component sessions
183          */
184         getType,
185         getCategory,
186         getInitialSubdomain,
187         getSubdomains,
188         getName,
189         getDescription,
190         start,
191         shutdown,
192 
193         /**
194          * Operations of incoming server sessions
195          */
196         getLocalDomain,
197         getAddress,
198         getValidatedDomains
199     }
200 }
201