Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

ServerRpcHandler.java 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. /*
  2. * Copyright 2000-2018 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server.communication;
  17. import java.io.IOException;
  18. import java.io.Reader;
  19. import java.io.Serializable;
  20. import java.lang.reflect.Type;
  21. import java.util.ArrayList;
  22. import java.util.HashSet;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Set;
  26. import java.util.logging.Level;
  27. import java.util.logging.Logger;
  28. import com.vaadin.server.ClientConnector;
  29. import com.vaadin.server.Constants;
  30. import com.vaadin.server.JsonCodec;
  31. import com.vaadin.server.LegacyCommunicationManager;
  32. import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException;
  33. import com.vaadin.server.ServerRpcManager;
  34. import com.vaadin.server.ServerRpcManager.RpcInvocationException;
  35. import com.vaadin.server.ServerRpcMethodInvocation;
  36. import com.vaadin.server.VaadinRequest;
  37. import com.vaadin.server.VaadinService;
  38. import com.vaadin.server.VariableOwner;
  39. import com.vaadin.shared.ApplicationConstants;
  40. import com.vaadin.shared.Connector;
  41. import com.vaadin.shared.Version;
  42. import com.vaadin.shared.communication.LegacyChangeVariablesInvocation;
  43. import com.vaadin.shared.communication.MethodInvocation;
  44. import com.vaadin.shared.communication.ServerRpc;
  45. import com.vaadin.shared.communication.UidlValue;
  46. import com.vaadin.shared.data.DataRequestRpc;
  47. import com.vaadin.ui.Component;
  48. import com.vaadin.ui.ConnectorTracker;
  49. import com.vaadin.ui.UI;
  50. import elemental.json.JsonArray;
  51. import elemental.json.JsonException;
  52. import elemental.json.JsonObject;
  53. import elemental.json.JsonValue;
  54. import elemental.json.impl.JsonUtil;
  55. /**
  56. * Handles a client-to-server message containing serialized {@link ServerRpc
  57. * server RPC} invocations.
  58. *
  59. * @author Vaadin Ltd
  60. * @since 7.1
  61. */
  62. public class ServerRpcHandler implements Serializable {
  63. /**
  64. * A data transfer object representing an RPC request sent by the client
  65. * side.
  66. *
  67. * @since 7.2
  68. * @author Vaadin Ltd
  69. */
  70. public static class RpcRequest implements Serializable {
  71. private final String csrfToken;
  72. private final JsonArray invocations;
  73. private final int syncId;
  74. private final JsonObject json;
  75. private final boolean resynchronize;
  76. private final int clientToServerMessageId;
  77. private String widgetsetVersion = null;
  78. public RpcRequest(String jsonString, VaadinRequest request) {
  79. json = JsonUtil.parse(jsonString);
  80. JsonValue token = json.get(ApplicationConstants.CSRF_TOKEN);
  81. if (token == null) {
  82. csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
  83. } else {
  84. String csrfToken = token.asString();
  85. if (csrfToken.isEmpty()) {
  86. csrfToken = ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE;
  87. }
  88. this.csrfToken = csrfToken;
  89. }
  90. if (request.getService().getDeploymentConfiguration()
  91. .isSyncIdCheckEnabled()) {
  92. syncId = (int) json
  93. .getNumber(ApplicationConstants.SERVER_SYNC_ID);
  94. } else {
  95. syncId = -1;
  96. }
  97. if (json.hasKey(ApplicationConstants.RESYNCHRONIZE_ID)) {
  98. resynchronize = json
  99. .getBoolean(ApplicationConstants.RESYNCHRONIZE_ID);
  100. } else {
  101. resynchronize = false;
  102. }
  103. if (json.hasKey(ApplicationConstants.WIDGETSET_VERSION_ID)) {
  104. widgetsetVersion = json
  105. .getString(ApplicationConstants.WIDGETSET_VERSION_ID);
  106. }
  107. if (json.hasKey(ApplicationConstants.CLIENT_TO_SERVER_ID)) {
  108. clientToServerMessageId = (int) json
  109. .getNumber(ApplicationConstants.CLIENT_TO_SERVER_ID);
  110. } else {
  111. getLogger()
  112. .warning("Server message without client id received");
  113. clientToServerMessageId = -1;
  114. }
  115. invocations = json.getArray(ApplicationConstants.RPC_INVOCATIONS);
  116. }
  117. /**
  118. * Gets the CSRF security token (double submit cookie) for this request.
  119. *
  120. * @return the CSRF security token for this current change request
  121. */
  122. public String getCsrfToken() {
  123. return csrfToken;
  124. }
  125. /**
  126. * Gets the data to recreate the RPC as requested by the client side.
  127. *
  128. * @return the data describing which RPC should be made, and all their
  129. * data
  130. */
  131. public JsonArray getRpcInvocationsData() {
  132. return invocations;
  133. }
  134. /**
  135. * Gets the sync id last seen by the client.
  136. *
  137. * @return the last sync id given by the server, according to the
  138. * client's request
  139. */
  140. public int getSyncId() {
  141. return syncId;
  142. }
  143. /**
  144. * Checks if this is a request to resynchronize the client side.
  145. *
  146. * @return true if this is a resynchronization request, false otherwise
  147. */
  148. public boolean isResynchronize() {
  149. return resynchronize;
  150. }
  151. /**
  152. * Gets the id of the client to server message.
  153. *
  154. * @since 7.6
  155. * @return the server message id
  156. */
  157. public int getClientToServerId() {
  158. return clientToServerMessageId;
  159. }
  160. /**
  161. * Gets the entire request in JSON format, as it was received from the
  162. * client.
  163. * <p>
  164. * <em>Note:</em> This is a shared reference - any modifications made
  165. * will be shared.
  166. *
  167. * @return the raw JSON object that was received from the client
  168. *
  169. */
  170. public JsonObject getRawJson() {
  171. return json;
  172. }
  173. /**
  174. * Gets the widget set version reported by the client.
  175. *
  176. * @since 7.6
  177. * @return The widget set version reported by the client or null if the
  178. * message did not contain a widget set version
  179. */
  180. public String getWidgetsetVersion() {
  181. return widgetsetVersion;
  182. }
  183. }
  184. private static final int MAX_BUFFER_SIZE = 64 * 1024;
  185. /**
  186. * Reads JSON containing zero or more serialized RPC calls (including legacy
  187. * variable changes) and executes the calls.
  188. *
  189. * @param ui
  190. * The {@link UI} receiving the calls. Cannot be null.
  191. * @param reader
  192. * The {@link Reader} used to read the JSON.
  193. * @param request
  194. * @throws IOException
  195. * If reading the message fails.
  196. * @throws InvalidUIDLSecurityKeyException
  197. * If the received security key does not match the one stored in
  198. * the session.
  199. */
  200. public void handleRpc(UI ui, Reader reader, VaadinRequest request)
  201. throws IOException, InvalidUIDLSecurityKeyException {
  202. ui.getSession().setLastRequestTimestamp(System.currentTimeMillis());
  203. String changeMessage = getMessage(reader);
  204. if (changeMessage == null || changeMessage.isEmpty()) {
  205. // The client sometimes sends empty messages, this is probably a bug
  206. return;
  207. }
  208. RpcRequest rpcRequest = new RpcRequest(changeMessage, request);
  209. // Security: double cookie submission pattern unless disabled by
  210. // property
  211. if (!VaadinService.isCsrfTokenValid(ui.getSession(),
  212. rpcRequest.getCsrfToken())) {
  213. throw new InvalidUIDLSecurityKeyException("");
  214. }
  215. checkWidgetsetVersion(rpcRequest.getWidgetsetVersion());
  216. int expectedId = ui.getLastProcessedClientToServerId() + 1;
  217. if (rpcRequest.getClientToServerId() != -1
  218. && rpcRequest.getClientToServerId() != expectedId) {
  219. // Invalid message id, skip RPC processing but force a full
  220. // re-synchronization of the client as it might have not received
  221. // the previous response (e.g. due to a bad connection)
  222. // Must resync also for duplicate messages because the server might
  223. // have generated a response for the first message but the response
  224. // did not reach the client. When the client re-sends the message,
  225. // it would only get an empty response (because the dirty flags have
  226. // been cleared on the server) and would be out of sync
  227. ui.getSession().getCommunicationManager().repaintAll(ui);
  228. if (rpcRequest.getClientToServerId() < expectedId) {
  229. // Just a duplicate message due to a bad connection or similar
  230. // It has already been handled by the server so it is safe to
  231. // ignore
  232. getLogger()
  233. .fine("Ignoring old message from the client. Expected: "
  234. + expectedId + ", got: "
  235. + rpcRequest.getClientToServerId());
  236. } else {
  237. getLogger().warning(
  238. "Unexpected message id from the client. Expected: "
  239. + expectedId + ", got: "
  240. + rpcRequest.getClientToServerId());
  241. }
  242. } else {
  243. // Message id ok, process RPCs
  244. ui.setLastProcessedClientToServerId(expectedId);
  245. handleInvocations(ui, rpcRequest.getSyncId(),
  246. rpcRequest.getRpcInvocationsData());
  247. }
  248. if (rpcRequest.isResynchronize()) {
  249. ui.getSession().getCommunicationManager().repaintAll(ui);
  250. }
  251. }
  252. /**
  253. * Checks that the version reported by the client (widgetset) matches that
  254. * of the server.
  255. *
  256. * @param widgetsetVersion
  257. * the widget set version reported by the client or null
  258. */
  259. private void checkWidgetsetVersion(String widgetsetVersion) {
  260. if (widgetsetVersion == null) {
  261. // Only check when the widgetset version is reported. It is reported
  262. // in the first UIDL request (not the initial request as it is a
  263. // plain GET /)
  264. return;
  265. }
  266. if (!Version.getFullVersion().equals(widgetsetVersion)) {
  267. getLogger().warning(String.format(Constants.WIDGETSET_MISMATCH_INFO,
  268. Version.getFullVersion(), widgetsetVersion));
  269. }
  270. }
  271. /**
  272. * Processes invocations data received from the client.
  273. * <p>
  274. * The invocations data can contain any number of RPC calls, including
  275. * legacy variable change calls that are processed separately.
  276. * <p>
  277. * Consecutive changes to the value of the same variable are combined and
  278. * changeVariables() is only called once for them. This preserves the Vaadin
  279. * 6 semantics for components and add-ons that do not use Vaadin 7 RPC
  280. * directly.
  281. *
  282. * @param ui
  283. * the UI receiving the invocations data
  284. * @param lastSyncIdSeenByClient
  285. * the most recent sync id the client has seen at the time the
  286. * request was sent
  287. * @param invocationsData
  288. * JSON containing all information needed to execute all
  289. * requested RPC calls.
  290. * @since 7.7
  291. */
  292. protected void handleInvocations(UI ui, int lastSyncIdSeenByClient,
  293. JsonArray invocationsData) {
  294. try {
  295. ConnectorTracker connectorTracker = ui.getConnectorTracker();
  296. Set<Connector> enabledConnectors = new HashSet<>();
  297. List<MethodInvocation> invocations = parseInvocations(
  298. ui.getConnectorTracker(), invocationsData,
  299. lastSyncIdSeenByClient);
  300. for (MethodInvocation invocation : invocations) {
  301. final ClientConnector connector = connectorTracker
  302. .getConnector(invocation.getConnectorId());
  303. if (connector != null && connector.isConnectorEnabled()) {
  304. enabledConnectors.add(connector);
  305. }
  306. }
  307. for (MethodInvocation invocation : invocations) {
  308. final ClientConnector connector = connectorTracker
  309. .getConnector(invocation.getConnectorId());
  310. if (connector == null) {
  311. logUnknownConnector(invocation.getConnectorId(),
  312. invocation.getInterfaceName(),
  313. invocation.getMethodName());
  314. continue;
  315. }
  316. if (!enabledConnectors.contains(connector)) {
  317. if (invocation instanceof LegacyChangeVariablesInvocation) {
  318. LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation;
  319. // TODO convert window close to a separate RPC call and
  320. // handle above - not a variable change
  321. // Handle special case where window-close is called
  322. // after the window has been removed from the
  323. // application or the application has closed
  324. Map<String, Object> changes = legacyInvocation
  325. .getVariableChanges();
  326. if (changes.size() == 1 && changes.containsKey("close")
  327. && Boolean.TRUE.equals(changes.get("close"))) {
  328. // Silently ignore this
  329. continue;
  330. }
  331. } else if (invocation instanceof ServerRpcMethodInvocation) {
  332. ServerRpcMethodInvocation rpc = (ServerRpcMethodInvocation) invocation;
  333. // special case for data communicator requesting more
  334. // data
  335. if (DataRequestRpc.class.getName()
  336. .equals(rpc.getInterfaceClass().getName())) {
  337. handleInvocation(ui, connector, rpc);
  338. }
  339. continue;
  340. }
  341. // Connector is disabled, log a warning and move to the next
  342. getLogger().warning(
  343. getIgnoredDisabledError("RPC call", connector));
  344. continue;
  345. }
  346. // DragAndDropService has null UI
  347. if (connector.getUI() != null
  348. && connector.getUI().isClosing()) {
  349. String msg = "Ignoring RPC call for connector "
  350. + connector.getClass().getName();
  351. if (connector instanceof Component) {
  352. String caption = ((Component) connector).getCaption();
  353. if (caption != null) {
  354. msg += ", caption=" + caption;
  355. }
  356. }
  357. msg += " in closed UI";
  358. getLogger().warning(msg);
  359. continue;
  360. }
  361. if (invocation instanceof ServerRpcMethodInvocation) {
  362. handleInvocation(ui, connector,
  363. (ServerRpcMethodInvocation) invocation);
  364. } else {
  365. LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation;
  366. handleInvocation(ui, connector, legacyInvocation);
  367. }
  368. }
  369. } catch (JsonException e) {
  370. getLogger().warning("Unable to parse RPC call from the client: "
  371. + e.getMessage());
  372. throw new RuntimeException(e);
  373. }
  374. }
  375. private void logUnknownConnector(String connectorId, String interfaceName,
  376. String methodName) {
  377. getLogger().log(Level.FINE,
  378. "Received RPC call for unknown connector with id {0} (tried to invoke {1}.{2})",
  379. new Object[] { connectorId, interfaceName, methodName });
  380. }
  381. /**
  382. * Handles the given RPC method invocation for the given connector.
  383. *
  384. * @since 7.7
  385. * @param ui
  386. * the UI containing the connector
  387. * @param connector
  388. * the connector the RPC is targeted to
  389. * @param invocation
  390. * information about the rpc to invoke
  391. */
  392. protected void handleInvocation(UI ui, ClientConnector connector,
  393. ServerRpcMethodInvocation invocation) {
  394. try {
  395. ServerRpcManager.applyInvocation(connector, invocation);
  396. } catch (RpcInvocationException e) {
  397. ui.getSession().getCommunicationManager()
  398. .handleConnectorRelatedException(connector, e);
  399. }
  400. }
  401. /**
  402. * Handles the given Legacy variable change RPC method invocation for the
  403. * given connector.
  404. *
  405. * @since 7.7
  406. * @param ui
  407. * the UI containing the connector
  408. * @param connector
  409. * the connector the RPC is targeted to
  410. * @param legacyInvocation
  411. * information about the rpc to invoke
  412. */
  413. protected void handleInvocation(UI ui, ClientConnector connector,
  414. LegacyChangeVariablesInvocation legacyInvocation) {
  415. Map<String, Object> changes = legacyInvocation.getVariableChanges();
  416. try {
  417. if (connector instanceof VariableOwner) {
  418. // The source parameter is never used anywhere
  419. changeVariables(null, (VariableOwner) connector, changes);
  420. } else {
  421. throw new IllegalStateException(
  422. "Received a legacy variable change for "
  423. + connector.getClass().getName() + " ("
  424. + connector.getConnectorId()
  425. + ") which is not a VariableOwner. The client-side connector sent these legacy variables: "
  426. + changes.keySet());
  427. }
  428. } catch (Exception e) {
  429. ui.getSession().getCommunicationManager()
  430. .handleConnectorRelatedException(connector, e);
  431. }
  432. }
  433. /**
  434. * Parse JSON from the client into a list of MethodInvocation instances.
  435. *
  436. * @param connectorTracker
  437. * The ConnectorTracker used to lookup connectors
  438. * @param invocationsJson
  439. * JSON containing all information needed to execute all
  440. * requested RPC calls.
  441. * @param lastSyncIdSeenByClient
  442. * the most recent sync id the client has seen at the time the
  443. * request was sent
  444. * @return list of MethodInvocation to perform
  445. */
  446. private List<MethodInvocation> parseInvocations(
  447. ConnectorTracker connectorTracker, JsonArray invocationsJson,
  448. int lastSyncIdSeenByClient) {
  449. int invocationCount = invocationsJson.length();
  450. List<MethodInvocation> invocations = new ArrayList<>(invocationCount);
  451. MethodInvocation previousInvocation = null;
  452. // parse JSON to MethodInvocations
  453. for (int i = 0; i < invocationCount; ++i) {
  454. JsonArray invocationJson = invocationsJson.getArray(i);
  455. MethodInvocation invocation = parseInvocation(invocationJson,
  456. previousInvocation, connectorTracker,
  457. lastSyncIdSeenByClient);
  458. if (invocation != null) {
  459. // Can be null if the invocation was a legacy invocation and it
  460. // was merged with the previous one or if the invocation was
  461. // rejected because of an error.
  462. invocations.add(invocation);
  463. previousInvocation = invocation;
  464. }
  465. }
  466. return invocations;
  467. }
  468. private MethodInvocation parseInvocation(JsonArray invocationJson,
  469. MethodInvocation previousInvocation,
  470. ConnectorTracker connectorTracker, long lastSyncIdSeenByClient) {
  471. String connectorId = invocationJson.getString(0);
  472. String interfaceName = invocationJson.getString(1);
  473. String methodName = invocationJson.getString(2);
  474. JsonArray parametersJson = invocationJson.getArray(3);
  475. if (LegacyChangeVariablesInvocation
  476. .isLegacyVariableChange(interfaceName, methodName)) {
  477. if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) {
  478. previousInvocation = null;
  479. }
  480. return parseLegacyChangeVariablesInvocation(connectorId,
  481. interfaceName, methodName,
  482. (LegacyChangeVariablesInvocation) previousInvocation,
  483. parametersJson, connectorTracker);
  484. } else {
  485. return parseServerRpcInvocation(connectorId, interfaceName,
  486. methodName, parametersJson, connectorTracker);
  487. }
  488. }
  489. private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation(
  490. String connectorId, String interfaceName, String methodName,
  491. LegacyChangeVariablesInvocation previousInvocation,
  492. JsonArray parametersJson, ConnectorTracker connectorTracker) {
  493. if (parametersJson.length() != 2) {
  494. throw new JsonException(
  495. "Invalid parameters in legacy change variables call. Expected 2, was "
  496. + parametersJson.length());
  497. }
  498. String variableName = parametersJson.getString(0);
  499. UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType(
  500. UidlValue.class, true, parametersJson.get(1), connectorTracker);
  501. Object value = uidlValue.getValue();
  502. if (previousInvocation != null
  503. && previousInvocation.getConnectorId().equals(connectorId)) {
  504. previousInvocation.setVariableChange(variableName, value);
  505. return null;
  506. } else {
  507. return new LegacyChangeVariablesInvocation(connectorId,
  508. variableName, value);
  509. }
  510. }
  511. private ServerRpcMethodInvocation parseServerRpcInvocation(
  512. String connectorId, String interfaceName, String methodName,
  513. JsonArray parametersJson, ConnectorTracker connectorTracker)
  514. throws JsonException {
  515. ClientConnector connector = connectorTracker.getConnector(connectorId);
  516. if (connector == null) {
  517. logUnknownConnector(connectorId, interfaceName, methodName);
  518. return null;
  519. }
  520. ServerRpcManager<?> rpcManager = connector.getRpcManager(interfaceName);
  521. if (rpcManager == null) {
  522. /*
  523. * Security: Don't even decode the json parameters if no RpcManager
  524. * corresponding to the received method invocation has been
  525. * registered.
  526. */
  527. String message = "Ignoring RPC call to " + interfaceName + "."
  528. + methodName + " in connector "
  529. + connector.getClass().getName() + "(" + connectorId
  530. + ") as no RPC implementation is registered";
  531. assert rpcManager != null : message;
  532. getLogger().warning(message);
  533. return null;
  534. }
  535. // Use interface from RpcManager instead of loading the class based on
  536. // the string name to avoid problems with OSGi
  537. Class<? extends ServerRpc> rpcInterface = rpcManager.getRpcInterface();
  538. ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
  539. connectorId, rpcInterface, methodName, parametersJson.length());
  540. Object[] parameters = new Object[parametersJson.length()];
  541. Type[] declaredRpcMethodParameterTypes = invocation.getMethod()
  542. .getGenericParameterTypes();
  543. for (int j = 0; j < parametersJson.length(); ++j) {
  544. JsonValue parameterValue = parametersJson.get(j);
  545. Type parameterType = declaredRpcMethodParameterTypes[j];
  546. parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
  547. parameterValue, connectorTracker);
  548. }
  549. invocation.setParameters(parameters);
  550. return invocation;
  551. }
  552. protected void changeVariables(Object source, VariableOwner owner,
  553. Map<String, Object> m) {
  554. owner.changeVariables(source, m);
  555. }
  556. protected String getMessage(Reader reader) throws IOException {
  557. StringBuilder sb = new StringBuilder(MAX_BUFFER_SIZE);
  558. char[] buffer = new char[MAX_BUFFER_SIZE];
  559. while (true) {
  560. int read = reader.read(buffer);
  561. if (read == -1) {
  562. break;
  563. }
  564. sb.append(buffer, 0, read);
  565. }
  566. return sb.toString();
  567. }
  568. private static final Logger getLogger() {
  569. return Logger.getLogger(ServerRpcHandler.class.getName());
  570. }
  571. /**
  572. * Generates an error message when the client is trying to to something
  573. * ('what') with a connector which is disabled or invisible.
  574. *
  575. * @since 7.1.8
  576. * @param connector
  577. * the connector which is disabled (or invisible)
  578. * @return an error message
  579. */
  580. public static String getIgnoredDisabledError(String what,
  581. ClientConnector connector) {
  582. String msg = "Ignoring " + what + " for disabled connector "
  583. + connector.getClass().getName();
  584. if (connector instanceof Component) {
  585. String caption = ((Component) connector).getCaption();
  586. if (caption != null) {
  587. msg += ", caption=" + caption;
  588. }
  589. }
  590. return msg;
  591. }
  592. }