CAY-2076 Adding close() method to ROP API; Taking out some common methods to ROPUtil;
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/HttpClientConnection.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/HttpClientConnection.java
index 06b842c..4ab182e 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/rop/HttpClientConnection.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/HttpClientConnection.java
@@ -19,6 +19,7 @@
package org.apache.cayenne.rop;
import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.di.BeforeScopeEnd;
import org.apache.cayenne.event.EventBridge;
import org.apache.cayenne.event.EventBridgeFactory;
import org.apache.cayenne.remote.BaseConnection;
@@ -26,6 +27,8 @@
import org.apache.cayenne.remote.RemoteService;
import org.apache.cayenne.remote.RemoteSession;
+import java.rmi.RemoteException;
+
public class HttpClientConnection extends BaseConnection {
private RemoteService remoteService;
@@ -71,6 +74,11 @@
return createServerEventBridge(session);
}
+ @BeforeScopeEnd
+ public void shutdown() throws RemoteException {
+ remoteService.close();
+ }
+
protected synchronized void connect() {
if (session != null) {
return;
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java
index 6a0a8a6..943dca0 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/ProxyRemoteService.java
@@ -64,4 +64,13 @@
throw new RemoteException(e.getMessage(), e);
}
}
+
+ @Override
+ public void close() throws RemoteException {
+ try {
+ ropConnector.close();
+ } catch (IOException e) {
+ throw new RemoteException("Exception while closing ROP resources", e);
+ }
+ }
}
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java
index 5855cf8..38a0579 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPConnector.java
@@ -37,11 +37,16 @@
* Creates a new session with the specified or joins an existing one. This method is
* used to bootstrap collaborating clients of a single "group chat".
*/
- InputStream establishSharedSession(String name) throws IOException;
+ InputStream establishSharedSession(String sharedSessionName) throws IOException;
/**
* Processes message on a remote server, returning the result of such processing.
*/
InputStream sendMessage(byte[] message) throws IOException;
+
+ /**
+ * Close all resources related to ROP Connector.
+ */
+ void close() throws IOException;
}
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPUtil.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPUtil.java
new file mode 100644
index 0000000..cb5cca9
--- /dev/null
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/ROPUtil.java
@@ -0,0 +1,150 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.rop;
+
+import java.util.Map;
+
+public class ROPUtil {
+
+ public static String getLogConnect(String url, String username, boolean password) {
+ return getLogConnect(url, username, password, null);
+ }
+
+ public static String getLogConnect(String url, String username, boolean password, String sharedSessionName) {
+ StringBuilder log = new StringBuilder("Connecting to [");
+ if (username != null) {
+ log.append(username);
+
+ if (password) {
+ log.append(":*******");
+ }
+
+ log.append("@");
+ }
+
+ log.append(url);
+ log.append("]");
+
+ if (sharedSessionName != null) {
+ log.append(" - shared session '").append(sharedSessionName).append("'");
+ } else {
+ log.append(" - dedicated session.");
+ }
+
+ return log.toString();
+ }
+
+ public static String getLogDisconnect(String url, String username, boolean password) {
+ StringBuilder log = new StringBuilder("Disconnecting from [");
+ if (username != null) {
+ log.append(username);
+
+ if (password) {
+ log.append(":*******");
+ }
+
+ log.append("@");
+ }
+
+ log.append(url);
+ log.append("]");
+
+ return log.toString();
+ }
+
+ public static String getParamsAsString(Map<String, String> params) {
+ StringBuilder urlParams = new StringBuilder();
+
+ for (Map.Entry<String, String> entry : params.entrySet()) {
+ if (urlParams.length() > 0) {
+ urlParams.append('&');
+ }
+
+ urlParams.append(entry.getKey());
+ urlParams.append('=');
+ urlParams.append(entry.getValue());
+ }
+
+ return urlParams.toString();
+ }
+
+ public static String getBasicAuth(String username, String password) {
+ if (username != null && password != null) {
+ return "Basic " + base64(username + ":" + password);
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates the Base64 value.
+ */
+ public static String base64(String value) {
+ StringBuffer cb = new StringBuffer();
+
+ int i = 0;
+ for (i = 0; i + 2 < value.length(); i += 3) {
+ long chunk = (int) value.charAt(i);
+ chunk = (chunk << 8) + (int) value.charAt(i + 1);
+ chunk = (chunk << 8) + (int) value.charAt(i + 2);
+
+ cb.append(encode(chunk >> 18));
+ cb.append(encode(chunk >> 12));
+ cb.append(encode(chunk >> 6));
+ cb.append(encode(chunk));
+ }
+
+ if (i + 1 < value.length()) {
+ long chunk = (int) value.charAt(i);
+ chunk = (chunk << 8) + (int) value.charAt(i + 1);
+ chunk <<= 8;
+
+ cb.append(encode(chunk >> 18));
+ cb.append(encode(chunk >> 12));
+ cb.append(encode(chunk >> 6));
+ cb.append('=');
+ } else if (i < value.length()) {
+ long chunk = (int) value.charAt(i);
+ chunk <<= 16;
+
+ cb.append(encode(chunk >> 18));
+ cb.append(encode(chunk >> 12));
+ cb.append('=');
+ cb.append('=');
+ }
+
+ return cb.toString();
+ }
+
+ public static char encode(long d) {
+ d &= 0x3f;
+ if (d < 26)
+ return (char) (d + 'A');
+ else if (d < 52)
+ return (char) (d + 'a' - 26);
+ else if (d < 62)
+ return (char) (d + '0' - 52);
+ else if (d == 62)
+ return '+';
+ else
+ return '/';
+ }
+
+}
\ No newline at end of file
diff --git a/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
index 15a54ed..bdae52b 100644
--- a/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
+++ b/cayenne-client/src/main/java/org/apache/cayenne/rop/http/HttpROPConnector.java
@@ -22,6 +22,7 @@
import org.apache.cayenne.rop.HttpClientConnection;
import org.apache.cayenne.rop.ROPConnector;
import org.apache.cayenne.rop.ROPConstants;
+import org.apache.cayenne.rop.ROPUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -66,7 +67,7 @@
@Override
public InputStream establishSession() throws IOException {
if (logger.isInfoEnabled()) {
- logConnect(null);
+ logger.info(ROPUtil.getLogConnect(url, username, password != null));
}
Map<String, String> requestParams = new HashMap<>();
@@ -76,14 +77,14 @@
}
@Override
- public InputStream establishSharedSession(String name) throws IOException {
+ public InputStream establishSharedSession(String sharedSessionName) throws IOException {
if (logger.isInfoEnabled()) {
- logConnect(name);
+ logger.info(ROPUtil.getLogConnect(url, username, password != null, sharedSessionName));
}
Map<String, String> requestParams = new HashMap<>();
requestParams.put(ROPConstants.OPERATION_PARAMETER, ROPConstants.ESTABLISH_SHARED_SESSION_OPERATION);
- requestParams.put(ROPConstants.SESSION_NAME_PARAMETER, name);
+ requestParams.put(ROPConstants.SESSION_NAME_PARAMETER, sharedSessionName);
return doRequest(requestParams);
}
@@ -92,40 +93,35 @@
public InputStream sendMessage(byte[] message) throws IOException {
return doRequest(message);
}
-
- protected InputStream doRequest(Map<String, String> params) throws IOException {
- URLConnection connection = new URL(url).openConnection();
- StringBuilder urlParams = new StringBuilder();
+ @Override
+ public void close() throws IOException {
+ if (logger.isInfoEnabled()) {
+ logger.info(ROPUtil.getLogDisconnect(url, username, password != null));
+ }
+ }
- for (Map.Entry<String, String> entry : params.entrySet()) {
- if (urlParams.length() > 0) {
- urlParams.append('&');
- }
+ protected InputStream doRequest(Map<String, String> params) throws IOException {
+ URLConnection connection = new URL(url).openConnection();
- urlParams.append(entry.getKey());
- urlParams.append('=');
- urlParams.append(entry.getValue());
- }
+ if (readTimeout != null) {
+ connection.setReadTimeout(readTimeout.intValue());
+ }
- if (readTimeout != null) {
- connection.setReadTimeout(readTimeout.intValue());
- }
+ addAuthHeader(connection);
- addAuthHeader(connection);
+ connection.setDoOutput(true);
- connection.setDoOutput(true);
-
- connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- connection.setRequestProperty("charset", "utf-8");
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ connection.setRequestProperty("charset", "utf-8");
- try (OutputStream output = connection.getOutputStream()) {
- output.write(urlParams.toString().getBytes(StandardCharsets.UTF_8));
+ try (OutputStream output = connection.getOutputStream()) {
+ output.write(ROPUtil.getParamsAsString(params).getBytes(StandardCharsets.UTF_8));
output.flush();
- }
+ }
- return connection.getInputStream();
- }
+ return connection.getInputStream();
+ }
protected InputStream doRequest(byte[] data) throws IOException {
URLConnection connection = new URL(url).openConnection();
@@ -151,7 +147,7 @@
}
protected void addAuthHeader(URLConnection connection) {
- String basicAuth = getBasicAuth(username, password);
+ String basicAuth = ROPUtil.getBasicAuth(username, password);
if (basicAuth != null) {
connection.addRequestProperty("Authorization", basicAuth);
@@ -167,91 +163,4 @@
}
}
- public String getBasicAuth(String user, String password) {
- if (user != null && password != null) {
- return "Basic " + base64(user + ":" + password);
- }
-
- return null;
- }
-
- /**
- * Creates the Base64 value.
- */
- private String base64(String value) {
- StringBuffer cb = new StringBuffer();
-
- int i = 0;
- for (i = 0; i + 2 < value.length(); i += 3) {
- long chunk = (int) value.charAt(i);
- chunk = (chunk << 8) + (int) value.charAt(i + 1);
- chunk = (chunk << 8) + (int) value.charAt(i + 2);
-
- cb.append(encode(chunk >> 18));
- cb.append(encode(chunk >> 12));
- cb.append(encode(chunk >> 6));
- cb.append(encode(chunk));
- }
-
- if (i + 1 < value.length()) {
- long chunk = (int) value.charAt(i);
- chunk = (chunk << 8) + (int) value.charAt(i + 1);
- chunk <<= 8;
-
- cb.append(encode(chunk >> 18));
- cb.append(encode(chunk >> 12));
- cb.append(encode(chunk >> 6));
- cb.append('=');
- }
- else if (i < value.length()) {
- long chunk = (int) value.charAt(i);
- chunk <<= 16;
-
- cb.append(encode(chunk >> 18));
- cb.append(encode(chunk >> 12));
- cb.append('=');
- cb.append('=');
- }
-
- return cb.toString();
- }
-
- public static char encode(long d) {
- d &= 0x3f;
- if (d < 26)
- return (char) (d + 'A');
- else if (d < 52)
- return (char) (d + 'a' - 26);
- else if (d < 62)
- return (char) (d + '0' - 52);
- else if (d == 62)
- return '+';
- else
- return '/';
- }
-
- private void logConnect(String sharedSessionName) {
- StringBuilder log = new StringBuilder("Connecting to [");
- if (username != null) {
- log.append(username);
-
- if (password != null) {
- log.append(":*******");
- }
-
- log.append("@");
- }
-
- log.append(url);
- log.append("]");
-
- if (sharedSessionName != null) {
- log.append(" - shared session '").append(sharedSessionName).append("'");
- }
- else {
- log.append(" - dedicated session.");
- }
-
- logger.info(log.toString());
- }
}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
index b5efb89..f357846 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/remote/RemoteService.java
@@ -1,20 +1,20 @@
/*****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
****************************************************************/
package org.apache.cayenne.remote;
@@ -24,7 +24,7 @@
/**
* Interface of a Cayenne remote service.
- *
+ *
* @since 1.2
* @see org.apache.cayenne.rop.ROPServlet
*/
@@ -45,4 +45,10 @@
* Processes message on a remote server, returning the result of such processing.
*/
Object processMessage(ClientMessage message) throws RemoteException, Throwable;
+
+ /**
+ * Close remote service resources.
+ * @sine 4.0
+ */
+ void close() throws RemoteException;
}
diff --git a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
index 2a63fbb..3716227 100644
--- a/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
+++ b/cayenne-server/src/main/java/org/apache/cayenne/remote/service/BaseRemoteService.java
@@ -19,10 +19,6 @@
package org.apache.cayenne.remote.service;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataChannel;
import org.apache.cayenne.access.ClientServerChannel;
@@ -36,6 +32,11 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import java.rmi.RemoteException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* A generic implementation of an RemoteService. Can be subclassed to work with
* different remoting mechanisms, such as Hessian or JAXRPC.
@@ -97,6 +98,7 @@
*/
protected abstract ServerSession getServerSession();
+ @Override
public RemoteSession establishSession() {
logger.debug("Session requested by client");
@@ -106,6 +108,7 @@
return session;
}
+ @Override
public RemoteSession establishSharedSession(String name) {
logger.debug("Shared session requested by client. Group name: " + name);
@@ -116,6 +119,7 @@
return createServerSession(name).getSession();
}
+ @Override
public Object processMessage(ClientMessage message) throws Throwable {
if (message == null) {
@@ -150,6 +154,10 @@
}
}
+ @Override
+ public void close() throws RemoteException {
+ }
+
protected RemoteSession createRemoteSession(String sessionId, String name, boolean enableEvents) {
RemoteSession session = (enableEvents) ? new RemoteSession(sessionId, eventBridgeFactoryName,
eventBridgeParameters) : new RemoteSession(sessionId);
diff --git a/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java b/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
index 66892d2..f1ecab2 100644
--- a/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
+++ b/cayenne-server/src/test/java/org/apache/cayenne/remote/MockRemoteService.java
@@ -34,4 +34,7 @@
return null;
}
+ @Override
+ public void close() throws RemoteException {
+ }
}
diff --git a/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java b/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
index ef75d21..ddb00e5 100644
--- a/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
+++ b/tutorials/tutorial-rop-client/src/main/java/org/apache/cayenne/tutorial/persistent/client/Main.java
@@ -18,15 +18,15 @@
****************************************************************/
package org.apache.cayenne.tutorial.persistent.client;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.configuration.Constants;
import org.apache.cayenne.configuration.rop.client.ClientRuntime;
import org.apache.cayenne.query.ObjectSelect;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
public class Main {
public static void main(String[] args) {
@@ -43,6 +43,7 @@
newObjectsTutorial(context);
selectTutorial(context);
deleteTutorial(context);
+ runtime.shutdown();
}
static void newObjectsTutorial(ObjectContext context) {