RestEasy client: Invalid use of BasicClientConnManager: connection still allocated - resteasy

I am using RestEasy client to retrieve a list of entities from web server. This is my code:
#ApplicationScoped
public class RestHttpClient {
private ResteasyClient client;
#Inject
private ObjectMapper mapper;
#PostConstruct
public void initialize() {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 5000);
HttpConnectionParams.setSoTimeout(params, 5000);
HttpClient httpClient = new DefaultHttpClient(params);
this.client = new ResteasyClientBuilder().httpEngine(new ApacheHttpClient4Engine(httpClient)).build();
}
public <E> List<E> getList(final Class<E> resultClass, final String path,
MultivaluedMap<String, Object> queryParams) {
ResteasyWebTarget target = this.client.target(path);
Response response = null;
try {
response = target.queryParams(queryParams).request().get();
String jsonString = response.readEntity(String.class);
TypeFactory typeFactory = TypeFactory.defaultInstance();
List<E> list = this.mapper.readValue(
jsonString, typeFactory.constructCollectionType(ArrayList.class, resultClass));
return list;
} catch (Exception e) {
// Handle exception
} finally {
if (response != null)
response.close();
}
return null;
}
}
It works fine, but... if I call getList() method multiple times in quick succession, sometimes I get the error "Invalid use of BasicClientConnManager: connection still allocated". I can make the same sequence of calls over and over again, and it works at least 90% of the time, so it appears to be a race condition. I am closing the Response object in finally block, which should be enough to release all resources, but apparently it isn't. What else do I have to do to make sure the connection is released? I have found some answers on the net, but they are either too old or not RestEasy-specific. I am using resteasy-client 3.0.4.Final.

I guess you only have one instance of your class RestHttpClient, and all threads/requests are using the same object.
The default ResteasyClientBuilder does not use a connection pool. Which means you can have only one parallel connection at a time. A request needs to be returned before you can use the ResteasyClient a second time (error message "connection still allocated"). You can avoid that by increasing the connection pool size:
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder();
clientBuilder = clientBuilder.connectionPoolSize( 20 );
ResteasyWebTarget target = clientBuilder.build().target( "http://your.host" );
I am using the following RestClientFactory to set up a new client. It gives you a debug output of the raw response, specifies the keystore (needed for client ssl certificates), a connection pool and the option for the use of a proxy.
public class RestClientFactory {
public static class Options {
private final String baseUri;
private final String proxyHostname;
private final String proxyPort;
private final String keystore;
private final String keystorePassword;
private final String connectionPoolSize;
private final String connectionTTL;
public Options(String baseUri, String proxyHostname, String proxyPort) {
this.baseUri = baseUri;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.connectionPoolSize = "100";
this.connectionTTL = "500";
this.keystore = System.getProperty( "javax.net.ssl.keyStore" );
this.keystorePassword = System.getProperty( "javax.net.ssl.keyStorePassword" );
}
public Options(String baseUri, String proxyHostname, String proxyPort, String keystore, String keystorePassword, String connectionPoolSize, String connectionTTL) {
this.baseUri = baseUri;
this.proxyHostname = proxyHostname;
this.proxyPort = proxyPort;
this.connectionPoolSize = connectionPoolSize;
this.connectionTTL = connectionTTL;
this.keystore = keystore;
this.keystorePassword = keystorePassword;
}
}
private static Logger log = LoggerFactory.getLogger( RestClientFactory.class );
public static <T> T createClient(Options options, Class<T> proxyInterface) throws Exception {
log.info( "creating ClientBuilder using options {}", ReflectionToStringBuilder.toString( options ) );
ResteasyClientBuilder clientBuilder = new ResteasyClientBuilder();
ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
RegisterBuiltin.register( providerFactory );
providerFactory.getClientReaderInterceptorRegistry().registerSingleton( new ReaderInterceptor() {
#Override
public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
if (log.isDebugEnabled()) {
InputStream is = context.getInputStream();
String responseBody = IOUtils.toString( is );
log.debug( "received response:\n{}\n\n", responseBody );
context.setInputStream( new ByteArrayInputStream( responseBody.getBytes() ) );
}
return context.proceed();
}
} );
clientBuilder.providerFactory( providerFactory );
if (StringUtils.isNotBlank( options.proxyHostname ) && StringUtils.isNotBlank( options.proxyPort )) {
clientBuilder = clientBuilder.defaultProxy( options.proxyHostname, Integer.parseInt( options.proxyPort ) );
}
// why the fuck do you have to specify the keystore with RestEasy?
// not setting the keystore will result in not using the global one
if ((StringUtils.isNotBlank( options.keystore )) && (StringUtils.isNotBlank( options.keystorePassword ))) {
KeyStore ks;
ks = KeyStore.getInstance( KeyStore.getDefaultType() );
FileInputStream fis = new FileInputStream( options.keystore );
ks.load( fis, options.keystorePassword.toCharArray() );
fis.close();
clientBuilder = clientBuilder.keyStore( ks, options.keystorePassword );
// Not catching these exceptions on purpose
}
if (StringUtils.isNotBlank( options.connectionPoolSize )) {
clientBuilder = clientBuilder.connectionPoolSize( Integer.parseInt( options.connectionPoolSize ) );
}
if (StringUtils.isNotBlank( options.connectionTTL )) {
clientBuilder = clientBuilder.connectionTTL( Long.parseLong( options.connectionTTL ), TimeUnit.MILLISECONDS );
}
ResteasyWebTarget target = clientBuilder.build().target( options.baseUri );
return target.proxy( proxyInterface );
}
}
An example client interface:
public interface SimpleClient
{
#GET
#Path("basic")
#Produces("text/plain")
String getBasic();
}
Create a client:
SimpleClient client = RestClientFactory.createClient(
new RestClientFactory.Options(
"https://your.service.host",
"proxyhost",
"8080",
"keystore.jks",
"changeit",
"20",
"500"
)
,SimpleClient.class
);
See also:
http://docs.jboss.org/resteasy/docs/3.0-beta-3/userguide/html/RESTEasy_Client_Framework.html#d4e2049

Related

Codename One: 405 Method Not Allowed error

I had been developing and testing on the Codename One simulator and everything worked fine.
However, when I tested it on a real Android device, I get a 405 Method Not Allowed error. This happened on both a POST and GET request.
I suspect it is the #Consume and #Produces which are causing the problem. How do I fix this?
Here are my server side code:
#GET
#Path("/all/{language}")
#Produces("application/json")
public final Response getAllCelebrities(#PathParam("language") String language) {
String celebritiesJSONString = CelebrityActions.getAllCelebritiesNamesJSONString(language);
return Response.ok(celebritiesJSONString).build();
}
#POST
#Path("/login")
#Consumes("application/x-www-form-urlencoded")
#Produces("text/plain")
public final Response login(
#FormParam("loginid") String loginid,
#FormParam("password") String password
) {
System.out.println("login 0 started");
Long fanID;
try {
fanID = AccountsActions.login(loginid, password);
} catch (Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
if (fanID == null) {
return responseFanIDNotFoundError();
}
System.out.println("This is printed out!!!");
System.out.println("login 100 ended");
return Response.ok().build();
}
And here's my log upon execution of the login() method:
login 0 started
This is printed out!!!
login 100 ended
which means the server side method was ready to return a 200 response.
What is causing the Android client to show a 405 Method Not Allow error?
EDIT: I'm adding my cient-side code here:
(note that this one handles a cookie from a server)
public class Login extends PostConnection {
private final String LoginEndpoint = "account/login";
private String loginIDString;
private String loginPasswordString;
// Tested and works on simulator!
public Login(String loginIDString, String loginPasswordString) {
super();
endpoint = LoginEndpoint;
this.loginIDString = loginIDString;
this.loginPasswordString = loginPasswordString;
}
#Override
protected void prepareParametersMap() {
parametersMap = new HashMap<>();
parametersMap.put("loginid", loginIDString);
parametersMap.put("password", loginPasswordString);
}
}
public abstract class PostConnection extends PostPutConnection {
public PostConnection() {
super();
}
public boolean connect() throws IOException {
connectionRequest.setHttpMethod("POST");
return super.connect();
}
}
public abstract class PostPutConnection extends Connection {
protected HashMap<String, String> parametersMap;
public PostPutConnection() {
super();
}
protected static final void setPostParameters(ConnectionRequest connectionRequest, HashMap<String, String> parametersMap) {
Set<String> paramateterKeys = parametersMap.keySet();
Iterator<String> parameterKeysIterator = paramateterKeys.iterator();
while (parameterKeysIterator.hasNext()) {
String key = parameterKeysIterator.next();
String value = parametersMap.get(key);
connectionRequest.addArgument(key, value);
}
}
protected abstract void prepareParametersMap();
public boolean connect() throws IOException {
prepareParametersMap();
setPost();
setPostParameters();
return super.connect();
}
private void setPostParameters() {
setPostParameters(connectionRequest, parametersMap);
}
private final void setPost() {
connectionRequest.setPost(true);
}
}
public abstract class Connection {
private final static String protocol = "http";
private final static String domain = "192.168.0.109:20000";
protected ConnectionRequest connectionRequest;
protected String endpoint;
public Connection() {
super();
init();
}
protected void init() {
connectionRequest = new ConnectionRequest();
connectionRequest.setCookiesEnabled(true);
ConnectionRequest.setUseNativeCookieStore(true);
}
public boolean connect() throws IOException {
connectionRequest.setUrl(protocol + "://" + domain + "/" + endpoint);
NetworkManager.getInstance().addToQueueAndWait(connectionRequest);
int responseCode = getResponseCode();
return responseCode == 200 ? true : false;
}
private int getResponseCode() {
int responseCode = connectionRequest.getResponseCode();
return responseCode;
}
}
And another method below:
(note that this one does not handle cookies)
public class GetAllCelebrities extends GetConnection {
private final String GetCelebritiesEndpoint = "celebrity/all";
public GetAllCelebrities(String language) {
super();
endpoint = GetCelebritiesEndpoint + "/" + language;
}
}
public abstract class GetConnection extends Connection {
private Map<String, Object> responseData;
public GetConnection() {
super();
}
public boolean connect() throws IOException {
connectionRequest.setHttpMethod("GET");
boolean connectResult = super.connect();
if (!connectResult) {
return false;
}
responseData = getResponseResult();
return true;
}
private Map<String, Object> getResponseResult() throws IOException {
byte[] responseData = connectionRequest.getResponseData();
ByteArrayInputStream responseDataBAIS = new ByteArrayInputStream(responseData);
InputStreamReader responseDataISR = new InputStreamReader(responseDataBAIS, "UTF-8");
JSONParser responseDateJSONParser = new JSONParser();
Map<String, Object> responseResult = responseDateJSONParser.parseJSON(responseDataISR);
return responseResult;
}
public Map<String, Object> getResponseData() {
return responseData;
}
}
And it is called like:
private Map<String, Object> fetchCelebrities() throws IOException {
GetAllCelebrities getAllCelebrities = new GetAllCelebrities("en");
getAllCelebrities.connect();
return getAllCelebrities.getResponseData();
}
private boolean performLogin() throws IOException {
String loginIDString = loginID.getText();
String loginPasswordString = loginPassword.getText();
Login login = new Login(loginIDString, loginPasswordString);
boolean loginResult = login.connect();
return loginResult;
}
It's a bit hard to read all of this code but I'll venture a guess based on the server message. You've set the method to "PUT" along the way in the post put class and that isn't supported by the server yet.
The best way to debug these things is with the network monitor in the Simulator. Its shows the traffic and would have made these things mostly clear

android app development-passing parameter to database

I am trying to connect my app to database on localhost server.I can connect to it ut the problem is how to pass the parameter from app to php script.for eg i want all names having age less than 10 so i will pass the parameter to php.below is my code for connecting to database.please provide good reference
/* */
enter code here
public class TestExternalDatabaseActivity extends Activity {
/** Called when the activity is first created. */
TextView resultView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
StrictMode.enableDefaults(); //STRICT MODE ENABLED
resultView = (TextView) findViewById(R.id.result);
getData();
}
public void getData(){
String result = "";
InputStream isr = null;
try{
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://localhost/tahseen0amin/php/getAllCustomers.php"); //YOUR PHP SCRIPT ADDRESS
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
isr = entity.getContent();
}
catch(Exception e){
Log.e("log_tag", "Error in http connection "+e.toString());
resultView.setText("Couldnt connect to database");
}
//convert response to string
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(isr,"iso-8859-1"),8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
isr.close();
result=sb.toString();
}
catch(Exception e){
Log.e("log_tag", "Error converting result "+e.toString());
}
//parse json data
try {
String s = "";
JSONArray jArray = new JSONArray(result);
for(int i=0; i<jArray.length();i++){
JSONObject json = jArray.getJSONObject(i);
s = s +
"Name : "+json.getString("FirstName")+" "+json.getString("LastName")+"\n"+
"Age : "+json.getInt("Age")+"\n"+
"Mobile Using : "+json.getString("Mobile")+"\n\n";
}
resultView.setText(s);
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error Parsing Data "+e.toString());
}
}
}

Send JSon from Server to Client in GCM

I am Using GCM (Google Cloud Messaging).In that what i want i want to send J Son from the server side .On Client side I want to receive that for simple message i have done but i am stucked how could i pass J Son from the server side to the client side.
Please help me to resolve this.
This is my Server side code
public class GCMBroadcast extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String SENDER_ID = "";
private static final String ANDROID_DEVICE = "";
private List<String> androidTargets = new ArrayList<String>();
public GCMBroadcast() {
super();
androidTargets.add(ANDROID_DEVICE);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String collapseKey = "";
String userMessage = "";
try {
userMessage = request.getParameter("Message");
collapseKey = request.getParameter("CollapseKey");
} catch (Exception e) {
e.printStackTrace();
return;
}
Sender sender = new Sender(SENDER_ID);
Message message = new Message.Builder()
.collapseKey(collapseKey)
.addData("message", userMessage)
.build();
try {
MulticastResult result = sender.send(message, androidTargets, 1);
System.out.println("Response: " + result.getResults().toString());
if (result.getResults() != null) {
int canonicalRegId = result.getCanonicalIds();
if (canonicalRegId != 0) {
System.out.println("response " +canonicalRegId );
}
} else {
int error = result.getFailure();
System.out.println("Broadcast failure: " + error);
}
} catch (Exception e) {
e.printStackTrace();
}
request.setAttribute("CollapseKey", collapseKey);
request.setAttribute("Message", userMessage);
request.getRequestDispatcher("XX.jsp").forward(request, response);
}
}
Your payload (added to the Message by calls to addData) can only be name/value pairs. If you want to send a JSON, you can put a JSON string in the value of such name/value pair. Then you'll have to parse that JSON yourself in the client side.
For example :
.addData("message","{\"some_json_key\":\"some_json_value\"}")

Increasing heap by excessive use oft Java ScriptEngine (Jyhton)

We have a JavaEE application that uses jython to execute some python scripts. By and by the used heapspace gets bigger and bigger until there is no more heapspace left. In a heapdump i can se that there are a lot of Py*-classes.
So i wrote a small test-program:
TestApp
public class TestApp {
private final ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
private HashMap<String, ScriptEngine> scriptEngines = new HashMap<String, ScriptEngine>();
private final String scriptContainerPath = "";
public static void main(String[] args) throws InterruptedException {
int counter = 1;
while(true) {
System.out.println("iteration: " + counter);
TestApp testApp = new TestApp();
testApp.execute();
counter++;
Thread.sleep(100);
}
}
void execute() {
File scriptContainer = new File(scriptContainerPath);
File[] scripts = scriptContainer.listFiles();
if (scripts != null && scripts.length > 0) {
Arrays.sort(scripts, new Comparator<File>() {
#Override
public int compare(File file1, File file2) {
return file1.getName().compareTo(file2.getName());
}
});
for (File script : scripts) {
String engineName = ScriptExecutor.getEngineNameByExtension(script.getName());
if(!scriptEngines.containsKey(engineName)) {
scriptEngines.put(engineName, scriptEngineManager.getEngineByName(engineName));
}
ScriptEngine scriptEngine = scriptEngines.get(engineName);
try {
ScriptExecutor scriptExecutor = new ScriptExecutor(scriptEngine, script, null);
Boolean disqualify = scriptExecutor.getBooleanScriptValue("disqualify");
String reason = scriptExecutor.getStringScriptValue("reason");
System.out.println("disqualify: " + disqualify);
System.out.println("reason: " + reason);
} catch (Exception e) {
e.printStackTrace();
}
}
// cleanup
for(Map.Entry<String, ScriptEngine> entry : scriptEngines.entrySet()) {
ScriptEngine engine = entry.getValue();
engine.getContext().setErrorWriter(null);
engine.getContext().setReader(null);
engine.getContext().setWriter(null);
}
}
}
}
ScriptExecutor
public class ScriptExecutor {
private final static String pythonExtension = "py";
private final static String pythonEngine = "python";
private final ScriptEngine scriptEngine;
public ScriptExecutor(ScriptEngine se, File file, Map<String, Object> keyValues) throws FileNotFoundException, ScriptException {
scriptEngine = se;
if (keyValues != null) {
for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
scriptEngine.put(entry.getKey(), entry.getValue());
}
}
// execute script
Reader reader = null;
try {
reader = new FileReader(file);
scriptEngine.eval(reader);
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
// nothing to do
}
}
}
}
public Boolean getBooleanScriptValue(String key) {
// convert Object to Boolean
}
public String getStringScriptValue(String key) {
// convert Object to String
}
public static String getEngineNameByExtension(String fileName) {
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
if (pythonExtension.equalsIgnoreCase(extension)) {
System.out.println("Found engine " + pythonEngine + " for extension " + extension + ".");
return pythonEngine;
}
throw new RuntimeException("No suitable engine found for extension " + extension);
}
}
In the specified directory are 14 python scripts that all look like this:
disqualify = True
reason = "reason"
I start this program with the following VM-arguments:
-Xrs -Xms16M -Xmx16M -XX:MaxPermSize=32M -XX:NewRatio=3 -Dsun.rmi.dgc.client.gcInterval=300000 -Dsun.rmi.dgc.server.gcInterval=300000 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -server
These are the arguments our AppServer is running with. Only Xms, Xmx and MaxPermSize are smaller in my testcase.
When I run this application I can see that the CMS Old Gen pool increases to its max size. After that the Par Eden Space pool increases. In addition at any time the ParNewGC does not run anymore. The cleanup part improved the situation but didn't resolve the problem. Has anybody an idea why my heap isn't completly cleaned?
I think I have found a solution for my problem: I removed the JSR223 stuff und now use the PythonInterpreter directly.

Blackberry HTTP Connection

public final class MyScreen extends MainScreen {
/**
* Creates a new MyScreen object
*/
public MyScreen() {
MyScreen myScreen = new MyScreen();
String a = myScreen.getPage("http://www.google.com");
System.out.println("+++ " + a);
}
public void parse(String xml) {
}
public String getPage(String url) {
String response = "";
try {
StreamConnection s = (StreamConnection) Connector.open(url);
InputStream input = s.openInputStream();
byte[] data = new byte[256];
int len = 0;
StringBuffer raw = new StringBuffer();
while (-1 != (len = input.read(data))) {
raw.append(new String(data, 0, len));
}
response = raw.toString();
input.close();
s.close();
} catch (Exception e) {}
return response;
}
}
When I execute this program in my Blackberry simulator, I get a StackOverflow error.
How might I resolve this?
checkout this :
1). Http connection error on the blackberry real device
2). http://docs.blackberry.com/en/developers/deliverables/11938/CS_create_first_available_HTTP_connection_857706_11.jsp
this may help you.

Resources