Unloading Java classes using CustomClassLoader - classloader

I have been through many posts and questions but none of them had a absolute Java program to implement this.
Requirement : For some reasons my application loads the Common-codec 1.3.jar,
and later on ,within same jvm the process needs to use Common-code 1.10.jar which is of different version.
But since the previous classes are loaded and they have same package name ,reloading them with a Java program is not replacing the existing classes.
Here is the code (sample) i used to reload(replace) the existing keys,but found no luck as expected. Please let me know hoe this could be done by a Java example.
String pathToJar=/root/Desktop/commons-codec-1.10.jar";
JarFile jarFile = null;
try {
jarFile = new JarFile(pathToJar);
} catch (IOException e) {
e.printStackTrace();
}
Enumeration<JarEntry> e = jarFile.entries();
URL[] urls = new URL[0];
try {
urls = new URL[]{ new URL("jar:file:" + pathToJar+"!/") };
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
URLClassLoader cl = URLClassLoader.newInstance(urls);
while (e.hasMoreElements()) {
JarEntry je = e.nextElement();
if(je.isDirectory() || !je.getName().endsWith(".class")){
continue;
}
// -6 because of .class
String className = je.getName().substring(0,je.getName().length()-6);
className = className.replace(File.separatorChar, '.');
String check="org.apache.commons.codec.binary.Base32";
try {
Class c = cl.loadClass(className); // Excepting it to replace old files,but thats not happening
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
I was advised to write Custom class loader and unloading is possible via it.
Could some show some relevant code or process of doing it.

Class "unloading" may only occur when both the Class and the ClassLoader that defined it are eligible for garbage collection. Furthermore, any given loader can only define a class of a specific name once. Therefore (save for exotic workarounds such as instrumentation or JDK implementation internals), there is no such thing as a "reloading-capable class loader" -- at least not in the true sense of the term; in order to obtain n distinct "versions" of a class, C, defineClass(C) must be invoked on n distinct ClassLoader objects.
Let's examine the following trivial use case: We have an app.jar, (unsurprisingly) containing a class declaring a main method; and two copies of a lib.jar, each having a single class, LibApiImpl, encapsulating a single method that just prints a version-dependent string to stdout. Our goal is to merely reference two copies of the "library" class from our main and see two different strings being output. The remainder of this post demonstrates just two of all the possible approaches to satisfy this requirement.
Approach #1 - Manually instantiating ClassLoaders
The straightforward solution is to simply create a new URLClassLoader every time a different LibApiImpl needs to be loaded. No custom class loader implementation is required, and the default parent-first delegation model serves both the application and the library well. A couple of things to note:
The library JARs must not sit on the classpath, in order to not be discoverable by the default application class loader. Otherwise the application class loader will "favor" one over the other, and, due to the default delegation model, standard URLClassLoader children of the application loader will not be able to override their parent's opinion on the matter.
To access the library class, the 3-argument Class::forName must be employed, specifying the manually-instantiated class loader. The single-arg version will delegate to the application class loader, which (as per the former point) is of course unaware of the existence of the corresponding .class file.
If the reflectively-obtained class is to be cast to an interface, as will be the case herein, the interface must, as opposed to the implementation, reside on the classpath.
Demo code
package com.example.app;
import java.net.URL;
import java.net.URLClassLoader;
import com.example.lib.api.LibApi;
public class App {
public static void main(String... args) throws Exception {
try (URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:/path/to/lib1.jar") })) {
((LibApi) Class.forName("com.example.lib.impl.LibApiImpl", true, loader).newInstance()).printVersionInfo();
}
try (URLClassLoader loader = new URLClassLoader(new URL[] { new URL("file:/path/to/lib2.jar") })) {
((LibApi) Class.forName("com.example.lib.impl.LibApiImpl", true, loader).newInstance()).printVersionInfo();
}
}
}
package com.example.lib.api;
public interface LibApi {
void printVersionInfo();
}
package com.example.lib.impl;
import com.example.lib.api.LibApi;
public class LibApiImpl implements LibApi {
#Override
public void printVersionInfo() {
System.out.println("\n** lib " + getClass() + " / loaded by " + getClass().getClassLoader() + " **\n");
}
}
Packaging
Correct the paths in com.example.app.App; then produce the following 4 JARs:
An app.jar containing the com.example.app package.
A lib-api.jar containing the com.example.lib.api package.
Two "versions" (just export twice), lib1.jar and lib2.jar, each containing the com.example.lib.impl package.
Testing
Run as follows:
java -cp '/path/to/app.jar:/path/to/lib-api.jar' com.example.app.App
Sample output:
** lib class com.example.lib.impl.LibApiImpl / loaded by java.net.URLClassLoader#55f96302 **
** lib class com.example.lib.impl.LibApiImpl / loaded by java.net.URLClassLoader#135fbaa4 **
Approach #2 - Creating a "Master-Slave" loader
Sometimes the "parent-first" model is insufficient. As a contrived example, suppose that we wanted to obtain the "copy" of LibApiImpl that was last loaded by some child of the application class loader, without having to care about which child it actually was that defined the copy. In other words, we want the call Class.forName("com.example.lib.impl.LibApiImpl") to return the "most fresh" LibApiImpl version. But, unless we override the application class loader, or, more generally, the class loader of the App class, with a custom implementation, that call will always fail, because, under the default delegation model, delegation flows unidirectionally from lower-level loaders to their ancestors and not vice versa.
The application class loader implementation, given below, behaves as follows (refer to the Javadoc overview of the Loaders class for a concrete explanation): There is a "master" loader, serving as the application class loader, that may have a child, referred to as its "slave". The master is responsible for loading non-reloadable application classes from the classpath (app.jar and lib-api.jar, in this case), while the slave gets to load reloadable, non-classpath-residing ones (lib1.jar and lib2.jar). The communication between the two is bidirectional, and the loader that will ultimately define any given class, "fixed" or "reloadable", is always respectively the master and the slave, regardless the "initiating" loader, i.e., the loader on which the application called loadClass, or passed to Class::forName. Of course this is nothing but a toy implementation meant to (hopefully) illustrate how a different delegation scheme might look like, and is probably flawed in ways I have not even begun to imagine. A real-world implementation would, e.g., have to provide proper concurrency; provide compliant implementations of getResource et al.; address concerns of code accessibility, verification, and possibly code base privilege assignment; and allow extensibility by, and configuration of, multiple slaves, or even attachment of other children, potentially of arbitrary implementation, to the master, while preserving well-defined delegation semantics. The point of course being that it is generally too much effort to write a proper implementation, when there is OSGi and the like doing just that (among many others).
Demo code
package com.example.app;
import java.net.URL;
import com.example.app.Loaders.MasterLoader;
import com.example.lib.api.LibApi;
public class App2 {
public static void main(String... args) throws Exception {
MasterLoader loader = (MasterLoader) ClassLoader.getSystemClassLoader();
loader.setDebug(true);
loader.refresh(new URL[] { new URL("file:/path/to/lib1.jar") });
newLibApi().printVersionInfo();
loader.refresh(new URL[] { new URL("file:/path/to/lib2.jar") });
newLibApi().printVersionInfo();
}
static LibApi newLibApi() throws Exception {
return (LibApi) Class.forName("com.example.lib.impl.LibApiImpl").newInstance();
}
}
package com.example.app;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
/**
* Contains a "Master (parent)/Slave (child)" <code>ClassLoader</code> "duo".<br/>
* <br/>
* The class loading "protocol" adhered by the respective {#code loadClass(String, boolean)}
* implementations of the two is as follows:
* <ol>
* <li>If the name argument matches the name of the loader's class itself, return that class.
* Otherwise proceed.</li>
* <li>If a call to {#code findLoadedClass(name)} yields a non-null class, return that class.
* Otherwise proceed.</li>
* <li>Let <em>C</em> be either this loader's parent, if this is a "slave", or its child, if this is
* a "master". If <em>C</em> is non-null, have it execute steps (1), (2) itself. If a class gets
* produced, return that class. Otherwise (i.e., if there either is no result, or <em>C</em> is
* null) proceed.</li>
* <li>If the name argument refers to a potential bootstrap classpath class name, call
* {#code loadClass(name)} on the default system classloader (the "master's" parent). If the call
* succeeds, return that class. Otherwise proceed.</li>
* <li>If the name argument refers to a .class file under this loader's search path, read it, define
* and link a new class off of its contents, and return that "freshly-fefined" class. Otherwise
* proceed.</li>
* <li>Once again, let <em>C</em> be the loader specified in step (3). If non-null, have it execute
* step (5) on itself. If a class gets produced, return that class. Otherwise fail.</li>
* </ol>
*/
public class Loaders {
private static class SlaveLoader extends URLClassLoader {
static final Pattern BOOT_CLASS_PATH_RES_NAMES = Pattern.compile("((com\\.)?sun|java(x)?)\\..*");
static final URL[] EMPTY_SEARCH_PATH = new URL[0];
static final URL[] createSearchPath(String pathNames) {
if (pathNames != null) {
List<URL> searchPath = new ArrayList<>();
for (String pathName : pathNames.split(File.pathSeparator)) {
try {
searchPath.add(Paths.get(pathName).toUri().toURL());
}
catch (MalformedURLException e) {
e.printStackTrace();
}
}
return searchPath.toArray(new URL[0]);
}
return EMPTY_SEARCH_PATH;
}
static final byte[] readClassData(URL classResource) throws IOException {
try (InputStream in = classResource.openStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {
while (in.available() > 0) {
out.write(in.read());
}
return out.toByteArray();
}
}
final String loadClassOutcomeMsgFmt = "loadClass return '{'\n\tloader = {0}\n\ttarget = {1}\n\tresult : {2}\n'}'";
volatile boolean debug;
SlaveLoader(URL[] searchPath, ClassLoader parent) {
super(searchPath, parent);
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
validateName(name);
Class<?> ret = loadFromCache(name);
if (ret != null) {
return ret;
}
MasterLoader parent = (MasterLoader) getParent();
if ((ret = parent.loadFromCache(name)) != null) {
log(loadClassOutcomeMsgFmt, this, name, "3 - early return - pre-loaded/cached - via " + parent);
return ret;
}
if ((ret = loadFromBootClasspath(name)) != null) {
return ret;
}
if ((ret = loadFromSearchPath(name, resolve)) != null) {
return ret;
}
if ((ret = parent.loadFromSearchPath(name, resolve)) != null) {
log(loadClassOutcomeMsgFmt, this, name,
"6 - common/non-reloadable classpath delegation - via " + parent);
return ret;
}
if ((ret = parent.loadFromSearchPath(name, resolve)) != null) {
return ret;
}
throw createCnfe(name, null);
}
void validateName(String name) throws ClassNotFoundException {
log("loadClass entry '{'\n\tloader = {0}\n\ttarget = {1}\n'}'", this, name);
if ((name == null) || name.trim().isEmpty()) {
throw createCnfe(name, null);
}
}
Class<?> loadFromCache(String name) {
Class<?> ret = getClass();
if (ret.getName().equals(name)) {
log(loadClassOutcomeMsgFmt, this, name, "1 - early return - own class");
return ret;
}
if ((ret = findLoadedClass(name)) != null) {
log(loadClassOutcomeMsgFmt, this, name, "2 - early return - pre-loaded/cached");
return ret;
}
return null;
}
Class<?> loadFromBootClasspath(String name) {
if (BOOT_CLASS_PATH_RES_NAMES.matcher(name).matches()) {
ClassLoader defSysCl = ClassLoader.getSystemClassLoader().getParent();
try {
Class<?> ret = ClassLoader.getSystemClassLoader().getParent().loadClass(name);
log(loadClassOutcomeMsgFmt, this, name, "4 - bootstrap classpath delegation - via " + defSysCl);
return ret;
}
catch (ClassNotFoundException cnfe) {
}
}
return null;
}
Class<?> loadFromSearchPath(String name, boolean resolve) throws ClassNotFoundException {
Class<?> ret = null;
URL res = findResource(name.replace(".", "/") + ".class");
if (res != null) {
byte[] b;
try {
b = readClassData(res);
}
catch (IOException ioe) {
throw createCnfe(name, ioe);
}
ret = defineClass(name, b, 0, b.length);
if (resolve) {
resolveClass(ret);
}
log(loadClassOutcomeMsgFmt, this, name, "5 - freshly-defined from local search path");
return ret;
}
return null;
}
ClassNotFoundException createCnfe(String name, Throwable cause) throws ClassNotFoundException {
return new ClassNotFoundException(MessageFormat.format("Class loading : {0} : {1} : FAILED", this,
(name == null) ? "null" : name, cause));
}
void log(String msg, Object... args) {
if (debug) {
System.out.println(MessageFormat.format("\n" + msg + "\n", args));
}
}
public void setDebug(boolean debug) {
this.debug = debug;
}
#Override
protected void finalize() throws Throwable {
try {
close();
}
finally {
super.finalize();
}
log("ClassLoader finalization : {0}", this);
}
}
public static class MasterLoader extends SlaveLoader {
static final URL[] DEFAULT_CLASS_PATH = createSearchPath(System.getProperty("java.class.path"));
private URL[] reloadableSearchPath = EMPTY_SEARCH_PATH;
private volatile SlaveLoader slave;
public MasterLoader(ClassLoader parent) {
super(DEFAULT_CLASS_PATH, parent);
}
public synchronized void refresh(URL[] reloadableSearchPath) {
int len;
if ((reloadableSearchPath != null) && ((len = reloadableSearchPath.length) > 0)) {
List<URL> path = new ArrayList<>(len + 1);
for (int i = 0; i < len; i++) {
URL entry = reloadableSearchPath[i];
if (entry != null) {
path.add(entry);
}
}
this.reloadableSearchPath = (!path.isEmpty()) ? path.toArray(EMPTY_SEARCH_PATH) : EMPTY_SEARCH_PATH;
}
else {
this.reloadableSearchPath = EMPTY_SEARCH_PATH;
}
if (slave != null) {
try {
slave.close();
}
catch (IOException ioe) {
}
slave = null;
/*
* At least two calls to System::gc appear to be required in order for Class::forName to cease
* returning cached classes previously defined by slave and for which the master served as an
* intermediary, i.e., an "initiating loader".
*
* See also http://blog.hargrave.io/2007/09/classforname-caches-defined-class-in.html
*/
for (int i = 0; i < 2; i++) {
System.gc();
try {
Thread.sleep(100);
}
catch (InterruptedException ie) {
}
}
}
if (this.reloadableSearchPath != EMPTY_SEARCH_PATH) {
log("Class loader search path refresh : {0}\n\tSearch path = {1}", this,
Arrays.toString(this.reloadableSearchPath));
slave = new SlaveLoader(this.reloadableSearchPath, this);
slave.setDebug(debug);
}
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
validateName(name);
Class<?> ret = loadFromCache(name);
if (ret != null) {
return ret;
}
if ((slave != null) && ((ret = slave.loadFromCache(name)) != null)) {
log(loadClassOutcomeMsgFmt, this, name, "3 - early return - pre-loaded/cached - via " + slave);
return ret;
}
if ((ret = loadFromBootClasspath(name)) != null) {
return ret;
}
if ((ret = loadFromSearchPath(name, resolve)) != null) {
return ret;
}
if ((slave != null) && ((ret = slave.loadFromSearchPath(name, resolve)) != null)) {
log(loadClassOutcomeMsgFmt, this, name,
"6 - reloadable classpath delegation - via " + ret.getClassLoader());
return ret;
}
throw createCnfe(name, null);
}
}
}
Packaging
Once again correct the paths in com.example.app.App2; add App2 and com.example.app.Loaders to app.jar; and re-export. The other JARs should remain as in the former example.
Testing
Run as follows:
java -cp '/path/to/app.jar:/path/to/lib-api.jar' \
'-Djava.system.class.loader=com.example.app.Loaders$MasterLoader' \
com.example.app.App2
Sample output (loadClass debug omitted):
** lib class com.example.lib.impl.LibApiImpl / loaded by com.example.app.Loaders$SlaveLoader#7f31245a **
...
ClassLoader finalization : com.example.app.Loaders$SlaveLoader#7f31245a
...
** lib class com.example.lib.impl.LibApiImpl / loaded by com.example.app.Loaders$SlaveLoader#12a3a380 **

Related

C++ Difficulty Creating Instance of Class within Singleton Class

I have a fairly good template (as in snippet of code) I pull out whenever I need a singleton class. I am now trying to apply it within my project to allow me to control a single instance of a web server. I can make a web server without encasing it in my class. When I try to encase it within the class I'm apparently too unskilled to pull it off.
I've tried the obvious Googling and searching here. I've read relevant posts. I am sure this does not mean I have a unique problem, just that I've not figured out the right way to fix it. Here's what I am working with:
webserver.h:
#include <ESP8266WebServer.h>
#include <FS.h>
class WebServer {
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer server();
String getContentType(String);
bool handleFileRead(String);
public:
// Singleton Declarations
static WebServer* getInstance();
~WebServer() {instanceFlag = false;}
// Other Declarations
void initialize(int);
void handleLoop();
};
webserver.cpp:
#include "webserver.h"
bool WebServer::instanceFlag = false;
WebServer* WebServer::single = NULL;
WebServer* WebServer::getInstance() {
if(!instanceFlag) {
single = new WebServer();
instanceFlag = true;
return single;
} else {
return single;
}
}
void WebServer::initialize (int port) {
ESP8266WebServer server(port);
FS *filesystem;
filesystem->begin();
Serial.print("Open: http://");
Serial.print(WiFi.hostname().c_str());
Serial.println(".local");
server.onNotFound([]() {
if (!single->handleFileRead(single->server.uri())) {
single->server.send(404, "text/plain", "404: File not found.");
}
});
server.begin();
Serial.print("HTTP server started on port ");
Serial.print(port);
Serial.println(".");
}
String WebServer::getContentType(String filename) {
if (single->server.hasArg("download")) {
return "application/octet-stream";
} else if (filename.endsWith(".htm")) {
return "text/html";
} else if (filename.endsWith(".html")) {
return "text/html";
} else if (filename.endsWith(".css")) {
return "text/css";
} else if (filename.endsWith(".js")) {
return "application/javascript";
} else if (filename.endsWith(".png")) {
return "image/png";
} else if (filename.endsWith(".gif")) {
return "image/gif";
} else if (filename.endsWith(".jpg")) {
return "image/jpeg";
} else if (filename.endsWith(".ico")) {
return "image/x-icon";
} else if (filename.endsWith(".xml")) {
return "text/xml";
} else if (filename.endsWith(".pdf")) {
return "application/x-pdf";
} else if (filename.endsWith(".zip")) {
return "application/x-zip";
} else if (filename.endsWith(".gz")) {
return "application/x-gzip";
} else {
return "text/plain";
}
}
bool WebServer::handleFileRead(String path) {
Serial.println("handleFileRead: " + path);
if (path.endsWith("/")) {
path += "index.htm";
}
String contentType = getContentType(path);
String pathWithGz = path + ".gz";
if (filesystem->exists(pathWithGz) || filesystem->exists(path)) {
if (filesystem->exists(pathWithGz)) {
path += ".gz";
}
File file = filesystem->open(path, "r");
single->server.streamFile(file, contentType);
file.close();
return true;
}
return false;
}
void WebServer::handleLoop() {
single->server.handleClient();
}
The errors I am getting are all similar to the following:
src\webserver.cpp: In member function 'bool WebServer::handleFileRead(String)':
src\webserver.cpp:81:23: error: 'WebServer::single->WebServer::server' does not have class type
single->server.streamFile(file, contentType);
I get the idea of "does not have a class type", I just have no idea what it means here. In my mind, "single" is a pointer to the class so I'm unclear what that reference is not working.
Obviously, there are ample examples out there how to do a web server without encapsulating it. Other things I need to do for this project lend itself to creating that requirement.
There are some mistake in your code.
In webserver.h:
...
private:
// Singleton Declarations
static bool instanceFlag;
static WebServer *single;
WebServer() {}
// Other Declarations
FS *filesystem;
ESP8266WebServer *server; // <--- remove the parentheses and make it a pointer
String getContentType(String);
bool handleFileRead(String);
...
In webserver.cpp:
In WebServer::initialize I am guessing you want to initialize the class server and filesystem not locals, so it should probably look like this:
void WebServer::initialize (int port) {
server = new ESP8266WebServer(port);
filesystem = new FS();
...
}
And now everywhere you use the server you have to use the -> operator.
For example:
void WebServer::handleLoop() {
single->server->handleClient();
}
Please keep in mind that server and filesystem objects have to be deleted to avoid memory leaks.
EDIT:
You get the new error because FS has no constructor without arguments.
FS's constructor looks like this: FS(FSImplPtr impl) : _impl(impl) { }, here you can see that FSImplPtr is a typedef for std::shared_ptr<FileImpl>, so you need to provide this as a parameter.
It works your way, because SPIFFS's existence is declared here and is of type FS.
If you want to use SPIFFS, you have to use it like this: filesystem = &SPIFFS;, not like you mentioned in the comments (FS* filesystem = &SPIFFS;) because your way creates a new temporary variable named filesystem, and probably you expect to initiate the filesystem in the class, not a local one.

Accessing retrofit 2 data outside on response?

I am working on two apps, in one of my app "A" i applied retrofit 2.
This was the method i used to retrieve data.
But here in on Response the data retrieved in response body can be set to activity variables and can be used outside this method without getting null values.
public void fetch_information() {
ApiInterface = ApiClient.getApiClient().create(Api.class);
Call<List<City>> call = ApiInterface.GetCities();
call.enqueue(new Callback<List<City>>() {
#Override
public void onResponse(Call<List<City>> call, Response<List<City>> response) {
citylist = new ArrayList<City>();
citylist = response.body();
cities = new String[citylist.size()];
citiesid = new String[citylist.size()];
for (int i = 0; i < citylist.size(); i++) {
cities[i] = citylist.get(i).getCityName();
citiesid[i] = citylist.get(i).getCityId();
}
city_adapter = new ArrayAdapter<String>(Pay_Payment_X.this, android.R.layout.simple_list_item_1, cities);
city_adapter.setDropDownViewResource(R.layout.spinner_dropdown_layout);
City_Spinner.setAdapter(city_adapter);
}
#Override
public void onFailure(Call<List<City>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
after applying this method and on debugging this method i will retain values of varaibles "cities" and "citiesid"out side onResponse.
But applying retrofit 2 similarly on another app "B", i did the same thing for retrieving data on different URL.
ApiUtil.getServiceClass().getAllPost().enqueue(new Callback<List<ApiObject>>() {
#Override
public void onResponse(Call<List<ApiObject>> call, Response<List<ApiObject>> response) {
if (response.isSuccessful()) {
List<ApiObject> postList = response.body();
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName();
mGamesName.add(Name);
}
} catch (Exception e) {
}
Log.d(TAG, "Returned count " + postList.size());
NewAdapter adapter = new NewAdapter(getApplicationContext(), postList);
recyclerView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<List<ApiObject>> call, Throwable t) {
//showErrorMessage();
Log.d(TAG, "error loading from API");
}
});
the data is retrievable inside onResponse but outside it shows null.
So here variables are not retaining values.
Why is this happening?
the only thing came to mind is retrieving data can take time while your code lines are being read and finding null values as data has not been received yet.
Also to mention in app "A" the data retrieved is huge but in app "B" only 3 objects with string values.But still in app"A" data is retrievable.
In app 2 did this for resolving my issue.
public void doRequest( final ApiCallback callback){
ApiUtil.getServiceClass().getAllPost().enqueue(new Callback<List<ApiObject>>() {
#Override
public void onResponse(Call<List<ApiObject>> call, Response<List<ApiObject>> response) {
if (response.isSuccessful()) {
List<ApiObject> postList = response.body();
callback.onSuccess(postList);
// apobject =response.body();
if(response.isSuccessful()) {
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName().toString();
mGamesName.add(Name);
}
} catch (Exception e) {
}
}
Log.d(TAG, "Returned count " + postList.size());
NewAdapter adapter = new NewAdapter(getApplicationContext(), postList);
recyclerView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<List<ApiObject>> call, Throwable t) {
//showErrorMessage();
Log.d(TAG, "error loading from API");
}
});
}
pass an interface
public interface ApiCallback{
void onSuccess(List<ApiObject> result);
}
and in on Create view of activity i called this
doRequest(new ApiCallback(){
#Override
public void onSuccess(List<ApiObject> result){
//here i can set variable values
}
});
the only thing came to mind is retrieving data can take time while your code lines are being read and finding null values as data has not been received yet.
That's entirely correct. Your call is finishing after you check the values. I'm going to go on a limb here and say that it's just a coincidence that it works on one app and not in the other (if they are actually doing it the same way)
When you call callback.onSuccess(postList); doesn't seem to be right either, because you haven't checked yet for success. This means that response.body() might be null and response.errorBody() will contain the body of the error.
If you'd move callback.onSuccess inside the if this would be fixed:
if(response.isSuccessful()) {
callback.onSuccess(response.body());
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName().toString();
mGamesName.add(Name);
}
} catch (Exception e) {
}
Last but not least, inside the onSuccess method is when you can use your global variables. Maybe it's better to stop using global variables and just use the callback parameters.

Simple program with Callable Future never terminates

I was playing around with Callable and Future and stumbled upon an issue.
This is a piece of code that never terminates and times out even though the IDE allows 5 seconds to run, and the code does not need more than 3 seconds (It gives a Time Limit Exceeded error): https://ideone.com/NcL0YV
/* package whatever; // don't place package name! */
import java.util.*;
import java.util.concurrent.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Ideone obj = new Ideone();
Future<Integer> res = obj.doCallable();
System.out.println(res.get());
}
public Future<Integer> calculate(Integer input) {
ExecutorService executor = Executors.newFixedThreadPool(1);
return executor.submit(() -> {
long start = System.currentTimeMillis();
Thread.sleep(2000);
System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
return input * input;
});
}
public Future<Integer> doCallable() {
int value = 99;
try {
Callable<Future> callable = () -> calculate(value);
Future<Integer> future = callable.call();
return future;
} catch (final Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
This is a similar piece of code that terminates because I added "System.exit(0)" (which is not advisable): https://ideone.com/HDvl7y
/* package whatever; // don't place package name! */
import java.util.*;
import java.util.concurrent.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
Ideone obj = new Ideone();
Future<Integer> res = obj.doCallable();
System.out.println(res.get());
System.exit(0);
}
public Future<Integer> calculate(Integer input) {
ExecutorService executor = Executors.newFixedThreadPool(1);
return executor.submit(() -> {
long start = System.currentTimeMillis();
Thread.sleep(2000);
System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start));
return input * input;
});
}
public Future<Integer> doCallable() {
int value = 99;
try {
Callable<Future> callable = () -> calculate(value);
Future<Integer> future = callable.call();
return future;
} catch (final Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
Please help me understand why do we need System.exit(0) or shutdown() even if callable task is complete (future.get() call is a blocking call).
EDIT:
I did all the above to solve the main issue of ever-increasing threads in my application because of the below code snippet. I am not sure how to complete this future automatically after a certain timeout without involving the main thread (which exits right away).
#Override
public void publish(#NonNull final String message,
#NonNull final String topicArn) throws PublishingException {
if (!publishAsync(message, topicArn)) {
throw new PublishingException("Publish attempt failed for the message:"
+ message);
}
}
private boolean publishAsync(final String message,
final String topicArn) {
Callable<Future> publishCallable = () -> snsClient.publishAsync(topicArn, message);
try {
Future<PublishResult> result = publishCallable.call();
log.debug("Asynchronously published message {} to SNS topic {}.", message, topicArn);
return !result.isDone() || result.get().getMessageId() != null;
} catch (final Exception e) {
return false;
}
}
void shutdown()
Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.
This method does not wait for previously submitted tasks to complete execution. Use awaitTerminationto do that.

How to custom a classloader

I'm trying to implementing this function with a customer classloader: I have some class files in a alternatives.jar file, they provide different implementation than normal implementation. i.e., each class in this jar, has another version which in other jar file -- also get loaded in the classpath.
I know it's better to use instrument API to achieve same purpose. But now my concern is I need to understand why I'm failing.
So this is my method:
1. define a AlternativeClassLoader.java, in this file, I override findClass method. So if the class name can be found from alternatives.jar, then use the version from alternatives.jar.
2. in constructor, I have called super(null) so all these class loading work will be performed by my classloader, rather that system's.
3. This (seems to be true) also requires me to load other classes (if they're not system one). So I have to parse classpath, find all classes which indicated by the classpath.
My problem is, I can load my alternative class, everything seems to be fine...However, I'm using slf4j which yells the following error:
Failed to auto configure default logger context
Reported exception:
ch.qos.logback.core.joran.spi.JoranException: Parser configuration error occurred
Failed to instantiate [ch.qos.logback.classic.LoggerContext]
Reported exception:
java.lang.ExceptionInInitializerError
at java.util.ResourceBundle.getLoader(ResourceBundle.java:431)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:841)
I doubt this is caused by my bad classloader implementation. Would somebody help me out? Many thanks!
This is my classloader:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private static final Logger logger = LoggerFactory
.getLogger(AlternativeClassLoader.class);
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
private Map<String, String> others = new HashMap<String, String>();
private Set<String> alternativesRegistry;
private JarFile altjar;
public AlternativeClassLoader(ClassLoader parent) {
/*
* pass null so I can incept all class loading except system's. By doing
* this you'll need to override findClass
*/
super(null);
registerAlternatives();
registerOthers();
}
/**
* This method will parse classpath and get all non-system class name, and
* build classname - jar_file_path/file_system_path mappings
*/
private void registerOthers() {
String[] paths = System.getProperty("java.class.path").split(":");
URL[] urls = new URL[paths.length];
for (String path : paths) {
if (path.endsWith("*.jar")) {
registerClass(path, others);
} else {
File f = new File(path);
if (!f.isDirectory())
continue;
File[] classFiles = f.listFiles(new FileFilter() {
#Override
public boolean accept(File arg0) {
if (arg0.getName().endsWith(".class")) {
return true;
} else {
return false;
}
}
});
for (File file : classFiles) {
String fileName = file.getName();
String className = fileName.substring(0,
fileName.lastIndexOf("."));
others.put(className, file.getPath());
}
}
}
showRegistry(
"Me will also be responsible for loading the following classes:",
others);
}
private void registerClass(String path, Map<String, String> registry) {
try {
JarInputStream jis = new JarInputStream(new FileInputStream(path));
for (JarEntry entry = jis.getNextJarEntry(); entry != null; entry = jis
.getNextJarEntry()) {
if (entry.getName().endsWith(".class") && !entry.isDirectory()) {
StringBuilder className = new StringBuilder();
for (String part : entry.getName().split("/")) {
if (className.length() != 0)
className.append(".");
className.append(part);
if (part.endsWith(".class"))
className.setLength(className.length()
- ".class".length());
}
registry.put(className.toString(), path);
}
}
} catch (Exception e) {
e.printStackTrace(System.out);
logger.error(
"Failed when read/parse jar {}. Your class file may not been replaced by alternative implementation",
path, e);
}
}
/**
* Try to find alternative class implementation from jar file specified by
* ALTERNATIVE_JAR_PROPERTY. If it's not specified, then use same jar file
* where this classloader is loaded.
*/
private void registerAlternatives() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()) {
URL url = getClass().getProtectionDomain().getCodeSource()
.getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
try {
altjar = new JarFile(jarFilePath);
} catch (IOException e) {
logger.error("cannot read jar {}", jarFilePath);
return;
}
Map<String, String> registry = new HashMap<String, String>();
registerClass(jarFilePath, registry);
alternativesRegistry = registry.keySet();
showRegistry("===Found the following alternative class:===", registry);
}
private void showRegistry(String string, Map<String, String> registry) {
System.out.println(string);
for (String clzName : registry.keySet()) {
System.out.printf("Class:%30s ->%s\n", clzName,
registry.get(clzName));
}
}
private Class<?> myLoadClass(String name) throws IOException,
ClassFormatError {
logger.debug("myload class {}", name);
System.out.printf("myload class %s\n", name);
if (alternativesRegistry.contains(name) && altjar != null) {
JarEntry entry = altjar.getJarEntry(name + ".class");
InputStream is = altjar.getInputStream(entry);
return readClassData(name, is);
}
String path = others.get(name);
if (path == null || path.isEmpty()) {
return null;
}
if (path.endsWith(".jar")) {
JarFile jar = new JarFile(path);
JarEntry entry = jar.getJarEntry(name + ".class");
InputStream is = jar.getInputStream(entry);
return readClassData(name, is);
} else {// it's a folder, need to read clz from .class file
System.out.printf("file path for %s is %s\n", name, path);
InputStream is = new FileInputStream(new File(path));
return readClassData(name, is);
}
}
private Class<?> readClassData(String name, InputStream is)
throws IOException, ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by me\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findCachedClass(String name)
throws ClassNotFoundException {
Class<?> clz = clzCache.get(name);
if (clz == null) {
clz = findLoadedClass(name);
}
return clz;
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = findCachedClass(name);
if (cls == null) {
try {
cls = myLoadClass(name);
} catch (ClassFormatError | IOException e) {
logger.error("failed to load class {}", name, e);
System.out.printf("failed to load class %s\n", name);
e.printStackTrace();
}
}
return cls;
}
}
I have tried to override findResource(), but it's never called.
This is how I put my classloader into use:
java -Djava.system.class.loader=AlternativeClassLoader -classpath=.:./alternatives.jar:./slf4j-xxx.jar Test
OK, I solved the problem. The tricky is:
Never use any package other than java.*. Otherwise, it will cause recursively loading ...IllegalState error.
In your classloader constructor, load all the alternative class and cache them.
In your constructor, call super(parent) other than super(null), then you don't need to do all the class loading stuff, the parent classloader can do it for you.
in override findClass(), if the class can be found from cache (means they have alternative implementation), then return it, otherwise let super.findClass do the rest for you.
so the following is the source code:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
public AlternativeClassLoader(ClassLoader parent) {
super(parent);
loadAlternativeClasses();
}
private void loadAlternativeClasses() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()){
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
JarInputStream jis;
try {
jis = new JarInputStream(new FileInputStream(jarFilePath));
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null){
String className = entry.getName();
className = className.substring(0, className.length() - ".class".length());
System.out.printf("loading class from %s: %s\n", jarFilePath, className);
readClassData(className, jis);
}
} catch (IOException e) {
e.printStackTrace();
}
} private Class<?> readClassData(String name, InputStream is) throws IOException,
ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by myself\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = clzCache.get(name);
if (cls == null)
cls = super.findClass(name);
return cls;
}
}

XSS Filter to enctype="multipart/form-data" forms

I found the next code that prevent xss atacks. But it has a problem. It works fine with forms that have enctype="application/x-www-form-urlencoded", but not with forms that have enctype="multipart/form-data". I observe that getParameterValues() and rest of methods are not called.
//--- XSS Filter ---//
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* Servlet Filter implementation class XSSFilter
*/
public class XSSFilter implements Filter {
#SuppressWarnings("unused")
private FilterConfig filterConfig;
/**
* Default constructor.
*/
public XSSFilter() {
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(new RequestWrapperXSS((HttpServletRequest) request), response);
}
}
//--- RequestWrapperXSS ---//
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class RequestWrapperXSS extends HttpServletRequestWrapper {
public RequestWrapperXSS(HttpServletRequest servletRequest) {
super(servletRequest);
}
public String[] getParameterValues(String parameter) {
System.out.println("entra parameterValues");
String[] values = super.getParameterValues(parameter);
if (values == null) {
return null;
}
int count = values.length;
String[] encodedValues = new String[count];
for (int i = 0; i < count; i++) {
encodedValues[i] = cleanXSS(values[i]);
}
return encodedValues;
}
public String getParameter(String parameter) {
System.out.println("entra getParameter");
String value = super.getParameter(parameter);
if (value == null) {
return null;
}
return cleanXSS(value);
}
public String getHeader(String name) {
System.out.println("entra header");
String value = super.getHeader(name);
if (value == null)
return null;
return cleanXSS(value);
}
private String cleanXSS(String cadena) {
System.out.println("entra claean XSS");
StringBuffer sb = new StringBuffer(cadena.length());
// true if last char was blank
boolean lastWasBlankChar = false;
int len = cadena.length();
char c;
for (int i = 0; i < len; i++)
{
c = cadena.charAt(i);
if (c == ' ') {
// blank gets extra work,
// this solves the problem you get if you replace all
// blanks with , if you do that you loss
// word breaking
if (lastWasBlankChar) {
lastWasBlankChar = false;
sb.append(" ");
}
else {
lastWasBlankChar = true;
sb.append(' ');
}
}
else {
lastWasBlankChar = false;
//
// HTML Special Chars
if (c == '"')
sb.append(""");
else if (c == '&')
sb.append("&");
else if (c == '<')
sb.append("<");
else if (c == '>')
sb.append(">");
else if (c == '\n')
// Handle Newline
sb.append("<br/>");
else {
int ci = 0xffff & c;
if (ci < 160 )
// nothing special only 7 Bit
sb.append(c);
else {
// Not 7 Bit use the unicode system
sb.append("&#");
sb.append(new Integer(ci).toString());
sb.append(';');
}
}
}
}
return sb.toString();
}
}
In case of multipart/form-data requests, the data is available by getPart() and getParts() methods, not by getParameter(), getParameterValues() and consorts.
Note that those methods are introduced in Servlet 3.0 and that in older versions there is not any standard API facility to extract data from multipart/form-data requests. The defacto API which is been used for that instead is the well known Apache Commons FileUpload.
Unrelated to the concrete problem, this is IMO a bad way to prevent XSS. XSS should be prevented in the view side during redisplaying the user-controlled input, right there where it can harm. Escaping before processing the user-controlled input will only risk in double escaping because it's not the "standard" way of XSS prevention. The developers should just ensure that they always escape user-controlled data in the view side using JSTL <c:out> or fn:escapeXml() or any other MVC framework supplied facilities (JSF for example escapes everything by default).
See also
XSS prevention in JSP/Servlet web application