Recently I analyzed crash reports form my app and found several stack traces which points to okhttp
My app doesn't depend on okhttp explicitly.
AFAIK okhttp version depends on Android OS version, and okhttp library by itself placed on device
To help with troubleshooting I decided to log okhttp library version, and looks like I found several useful classes for this
com.squareup.okhttp.internal.Version
okhttp3.internal.Version
Just to make sure that I didn't mistake I took com.android.okhttp.internal.http.HttpURLConnectionImpl class form stack-trace and tried to Class.forName it - success
Also I noticed that com.squareup.okhttp transformed to com.android.okhttp looks like at build-time, so totally I tried such variants
Class.forName("com.android.okhttp.internal.Version") -> java.lang.ClassNotFoundException
Class.forName("com.squareup.okhttp.internal.Version") -> java.lang.ClassNotFoundException
Class.forName("okhttp3.internal.Version") -> java.lang.ClassNotFoundException
Class.forName("com.android.okhttp.internal.http.HttpURLConnectionImpl") -> success
Can anyone explain why? What I missed?
Update
I have pulled okhttp.jar from my device adb pull /system/framework/okhttp.jar but it contains MANIFEST.MF only
from 4.xx google is using okhttp part of squareup
/**
* This implementation uses HttpEngine to send requests and receive responses. This class may use
* multiple HttpEngines to follow redirects, authentication retries, etc. to retrieve the final
* response body.
*
* <h3>What does 'connected' mean?</h3> This class inherits a {#code connected} field from the
* superclass. That field is <strong>not</strong> used to indicate whether this URLConnection is
* currently connected. Instead, it indicates whether a connection has ever been attempted. Once a
* connection has been attempted, certain properties (request header fields, request method, etc.)
* are immutable.
*/
public class HttpURLConnectionImpl extends HttpURLConnection {
private String defaultUserAgent() {
String agent = System.getProperty("http.agent");
return agent != null ? Util.toHumanReadableAscii(agent) : Version.userAgent();
}
https://github.com/square/okhttp/blob/master/okhttp-urlconnection/src/main/java/okhttp3/internal/huc/HttpURLConnectionImpl.java
http://square.github.io/okhttp/
everything depends on device - what os version u using because api is evolving, u can use reflections but u need know what field is on specific api
see https://github.com/square/okhttp/blob/master/CHANGELOG.md
to compare diffrent api versions use: https://android.googlesource.com/platform/external/okhttp/
u can try at the beginning
System.getProperty("http.agent");
edit:
via reflections
HttpURLConnection connection = (HttpURLConnection) new URL("http://google.com")
.openConnection();
Method method = connection.getClass().getDeclaredMethod("defaultUserAgent");
method.setAccessible(true);
String okEngineVersion = (String) method.invoke(connection, new Object[]{});
same as
String okEngineVersion = System.getProperty("http.agent");
and if u want to bother:
every class is treated the same way - > as equals ( no versioning - u can only check magic minor major number - java compiler version from class)
manifest of /system/framework/okhttp.jar doesn't contain version properties
if u want okhttp.internal.Version class then:
File file = new File("/system/framework/okhttp.jar");
// using javaxt-core lib
Jar jar = new Jar(file);
jar.getVersion();
// load dex
DexFile dexfile = DexFile.loadDex(file.getAbsolutePath(),
File.createTempFile("opt", "dex", _context.getCacheDir()).getPath(), 0);
Enumeration<String> dexEntries = dexfile.entries();
ClassLoader systemClassLoader = DexClassLoader.getSystemClassLoader();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
Class<?> aClass = systemClassLoader.loadClass(className);
}
conclusion: If you want to avoid crash of app from library changes delivery
own version of library and load classes on the fly or compile with apk
Related
I am trying to update the jclouds libs we use from version 1.5 to 1.7.
We access the api the following way:
https://github.com/jclouds/jclouds-examples/tree/master/rackspace/src/main/java/org/jclouds/examples/rackspace/cloudfiles
private RestContext<CommonSwiftClient, CommonSwiftAsyncClient> swift;
BlobStoreContext context = ContextBuilder.newBuilder(PROVIDER)
.credentials(username, apiKey)
.buildView(BlobStoreContext.class);
swift = context.unwrap();
RestContext is deprecated since 1.6.
http://demobox.github.io/jclouds-maven-site-1.6.0/1.6.0/jclouds-multi/apidocs/org/jclouds/rest/RestContext.html
I tried to get it working this way:
ContextBuilder contextBuilder = ContextBuilder.newBuilder(rackspaceProvider)
.credentials(rackspaceUsername, rackspaceApiKey);
rackspaceApi = contextBuilder.buildApi(CloudFilesClient.class);
At runtime, uploading a file i get the following error:
org.jclouds.blobstore.ContainerNotFoundException
The examples in the jclouds github project seem to use the deprecated approach (Links mentioned above).
Any ideas how to solve this? Any alternatives?
Does the container that you're uploading into exist? The putObject method doesn't automatically create the container that you name if it doesn't exist; you need to call createContainer explicitly to create it, first.
Here's an example that creates a container and uploads a file into it:
CloudFilesClient client = ContextBuilder.newBuilder("cloudfiles-us")
.credentials(USERNAME, APIKEY)
.buildApi(CloudFilesClient.class);
client.createContainer("sample");
SwiftObject object = client.newSwiftObject();
object.getInfo().setName("somefile.txt");
object.setPayload("file or bytearray or something else here");
client.putObject("sample", object);
// ...
client.close();
You're right that the examples in jclouds-examples still reference RestClient, but you should be able to translate to the new style by substituting your rackspaceApi object where they call swift.getApi().
I have used EMC Documentum Foundation Classes to perform some actions in documentum repository. The code was working fine. I exported the project as a runnable JAR and then tried to run it. However I got following error and I am not able to understand it.
And here is the code for DocMovementHandler.getSession()
Actually this is no new code but regular code for obtaining documentum session
public IDfSession getSession(String userName, String password)
{
DfClientX clientx = null;
IDfClient client = null;
IDfSession session = null;
try {
// create a client object using a factory method in DfClientX
clientx = new DfClientX();
client = clientx.getLocalClient(); //takes time
// call a factory method to create the session manager
IDfSessionManager sessionMgr = client.newSessionManager();
// create an IDfLoginInfo object and set its fields
IDfLoginInfo loginInfo = clientx.getLoginInfo();
loginInfo.setUser(userName);
loginInfo.setPassword(password);
// set single identity for all docbases
sessionMgr.setIdentity("xyz_repo", loginInfo);
session = sessionMgr.getSession("xyz_repo"); //takes time
//sessionMgr.beginTransaction();
System.out.println("Session obtaied.");
}
catch (DfServiceException dse)
{
DfLogger.debug(this, "Error while beginning transaction. ", null, dse);
dse.printStackTrace();
}
catch (Exception e)
{
DfLogger.debug(this, "Error while creating a new session. ", null, e);
e.printStackTrace();
}
return session;
}
And that line 38 is client = clientx.getLocalClient();
InvocationTargetException is a wrapper. It says, "an exception occurred behind this reflection call", and you use getCause() to get at the inner exception.
The stack trace contains the inner exception. It's an ExceptionInInitializerError. That's another wrapper. It says, "whatever you did caused a new class to be loaded, and that class's static initializer threw an exception".
The final exception in this chain is the NullPointerException. That's the one you need to solve. Which means you need to debug this com.documentum thing. As the comments pointed out, that's not going to be easy.
Here is the most likely problem:
The static initializer in one of the classes whose names you have struck is adding an entry with either a null key or a null value to a Hashtable, which does not allow null keys or values.
It is using the Hashtable as a place to store a bunch of persistent properties and all that, and my guess is that the value for one of the entries was the null (which is a perfectly reasonable way to indicate that some feature is unavailable or something like that).
The now deprecated Hashtable needs to be replaced with the more modern HashMap.
If it is a library, that you can't just modify, you should replace the whole library with an updated version.
Here are some clues may be helpful.
The NullPointerException is thrown by Hashtable#put, and this is normally because either the key or the value is null.
Hashtable#put is called by PreferenceManager.readPersistenceProperties, so most likely it's because something is missing in a properties file so the value is null.
This NPE caused the DfClient class could not be loaded.
DfPreferences is the class loading the DFC configuration file dfc.properties. There must be something wrong with it.
Ohkay I did not pin pointed the root cause, but found the solution that will definitely work everytime.
EMC provides a flavor of Eclipse called Documentum Composer to work with Documentum Projects. Since Eclipse variation we can create other types of projects like normal Java project, dynamic web project, web services in this. So I recreated my project in Documetnum Composer and exported it as JAR and whoaaaa it worked.
I tried this many times and this worked all time.
Some points to note:
You have to replace dfc.properties file in Composer installation folder with one in Content Server
The Export to JAR wizard in Composer is a bit different than one in Eclipse
This is usually caused by dfc.properties being incorrect.
Preferences are stored on the global registry repository and the connection details should be specified in dfc.properties. If not, this (or a similar error can occur).
Also, always try to clear cache and use the correct version of the dfc jar's (v6.7 content server requires 6.7 jars, etc...).
We are using Google App Engine to publish some flat data from our application in a csv format. Google's DataTable and CsvRenderer from visualization-datasource library made the conversion of our data easy and testing succeeded in the local appengine development server.
Deploying to our appspot instance exposed that App Engine does not like the way IBM's ICU library is Logging:
Google App Engine does not support subclasses of java.util.logging.Logger: com/ibm/icu/impl/ICULogger
The stack trace shows the point of origin:
Caused by: java.lang.SecurityException: Google App Engine does not support subclasses of java.util.logging.Logger: com/ibm/icu/impl/ICULogger
at com.google.appengine.runtime.Request.process-0b3cd097cad783e6(Request.java)
at java.lang.ClassLoader.loadClass(ClassLoader.java:360)
at com.ibm.icu.util.TimeZone.<clinit>(TimeZone.java:114)
Line 114 of TimeZone:
/**
* {#icu} A logger for TimeZone. Will be null if logging is not on by way of system
* property: "icu4j.debug.logging"
* #draft ICU 4.4
* #provisional This API might change or be removed in a future release.
*/
public static ICULogger TimeZoneLogger = ICULogger.getICULogger(TimeZone.class.getName());
I put a breakpoint in Eclipse and ran the unit tests in the debugger and TimeZoneLogger is being assigned null since there is no System property turning on the logging as seen further:
/**
* Instantiates a new ICULogger object with logging turned off by default
* unless the system property "icu4j.debug.logging" is set to "all"
*
* #param name to be use by the logger (usually is the class name)
* #param resourceBundleName name to localize messages (can be null)
* #return a new ICULogger object
* #draft ICU 4.4
* #provisional This API might change or be removed in a future release.
*/
public static ICULogger getICULogger(String name, String resourceBundleName) {
LOGGER_STATUS flag = checkGlobalLoggingFlag();
if (flag != LOGGER_STATUS.NULL) {
ICULogger logger = new ICULogger(name, resourceBundleName);
/* Add a default handler to logger*/
logger.addHandler(new ConsoleHandler());
/* Turn off logging by default unless SYSTEM_PROP_LOGGER property is set to "all" */
if (flag == LOGGER_STATUS.ON) {
logger.turnOnLogging();
} else {
logger.turnOffLogging();
}
return logger;
}
return null;
}
I put in some logging statements to see if TimeZoneLogger was being instantiated and it shows it is not.
[INFO] Oct 29, 2013 8:45:39 AM com.example.SomeClass
[INFO] WARNING: icu4j.debug.logging = null
[INFO] Oct 29, 2013 8:45:39 AM com.example.SomeClass
[INFO] WARNING: Time Zone Logger = null
This shows that it's not the instantiation that App Engine doesn't like, but simply the reference to the class that causes the problem.
At this point all I can think of is to write my own CSV Renderer which is the class using the violating code. The effort would not be significant, but I would prefer to use existing libraries...especially when the library and platform both come from Google.
Any other suggestions?
You can provide your own implementation of the class to avoid this issue.
Simply take the com.ibm.icu.util.TimeZone.java file from the project (It's open source). Then put it into your own project - keeping the package and class name the same. "Your" version will then override the one from the jar library and you can change it.
If it doesn't then check your build path order for the project. On the productive server classes are before libs so it will also work.
I made these hacky modifications to get it to work but you might make better ones :)
line ~114:
public static Object TimeZoneLogger = null; // ICULogger.getICULogger(TimeZone.class.getName());
line ~780: comment this out too.
if (TimeZoneLogger != null && TimeZoneLogger.isLoggingOn()) {
TimeZoneLogger.warning(
"\"" +ID + "\" is a bogus id so timezone is falling back to Etc/Unknown(GMT).");
}
I need to show on my panel the working dir.
I use String value = System.getProperty("user.dir"). Afterwards i put this string on label but I receive this message on console:
The method getProperty(String, String) in the type System is not applicable for the arguments (String).
I use eclipse.
Issue
I am guessing you have not gone through GWT 101 - You cannot blindly use JAVA CODE on client side.
Explanation
You can find the list of classes and methods supported for GWT from JAVA.
https://developers.google.com/web-toolkit/doc/latest/RefJreEmulation
For System only the following are supported.
err, out,
System(),
arraycopy(Object, int, Object, int, int),
currentTimeMillis(),
gc(),
identityHashCode(Object),
setErr(PrintStream),
setOut(PrintStream)
Solution
In your case Execute System.getProperty("user.dir") in your server side code and access it using RPC or any other server side gwt communication technique.
System.getProperty("key") is not supported,
but System.getProperty("key", "default") IS supported, though it will only return the default value as there is not system properties per se.
If you need the working directory during gwt compile, you need to use a custom linker or generator, grab the system property at build time, and emit it as a public resource file.
For linkers, you have to export an external file that gwt can download and get the compile-time data you want. For generators, you just inject the string you want into compiled source.
Here's a slideshow on linkers that is actually very interesting.
http://dl.google.com/googleio/2010/gwt-gwt-linkers.pdf
If you don't want to use a linker and an extra http request, you can use a generator as well, which is likely much easier (and faster):
interface BuildData {
String workingDirectory();
}
BuildData data = GWT.create(BuildData.class);
data.workingDirectory();
Then, you need to make a generator:
public class BuildDataGenerator extends IncrementalGenerator {
#Override
public RebindResult generateIncrementally(TreeLogger logger,
GeneratorContext context, String typeName){
//generator boilerplate
PrintWriter printWriter = context.tryCreate(logger, "com.foo", "BuildDataImpl");
if (printWriter == null){
logger.log(Type.TRACE, "Already generated");
return new RebindResult(RebindMode.USE_PARTIAL_CACHED,"com.foo.BuildDataImpl");
}
SourceFileComposerFactory composer =
new SourceFileComposerFactory("com.foo", "BuildDataImpl");
//must implement interface we are generating to avoid class cast exception
composer.addImplementedInterface("com.foo.BuildData");
SourceWriter sw = composer.createSourceWriter(printWriter);
//write the generated class; the class definition is done for you
sw.println("public String workingDirectory(){");
sw.println("return \""+System.getProperty("user.dir")+"\";");
sw.println("}");
return new RebindResult(RebindMode.USE_ALL_NEW_WITH_NO_CACHING
,"com.foo.BuildDataImpl");
}
}
Finally, you need to tell gwt to use your generator on your interface:
<generate-with class="dev.com.foo.BuildDataGenerator">
<when-type-assignable class="com.foo.BuildData" />
</generate-with>
We have an existing Java Wicket 1.4 application which uses the HybridUrlCodingStrategy extensively:
mount(new HybridUrlCodingStrategy("/myurl", MyPage.class));
This results in our URL's looking like:
http://host/myurl/paramName1/paramValue1/paramName2/paramValue2
I would like to maintain this URL format in Wicket 1.5, however the HybridUrlCodingStrategy has been removed. In wicket 1.5, pages are mounted as:
mountPage("/myurl", MyPage.class);
Which results in traditional URLs like:
http://host/myurl?paramName1=paramValue2¶mName2=paramValue2
I have read that we should be using the MountedMapper class, but looking at the Wicket 1.5 examples, API docs, and source code, it is still not clear to me how to get the same behavior with MountedMapper as we are getting with the HybridUrlCodingStrategy.
Does anyone know how to do this?
Maybe something like this:
mountPage("/myurl/paramName1/${paramValue1}/paramName2/${paramValue2}", MyPage.class)
would work? Granted, you'd have to manually specify your parameters, which could be a lot more work.
The MountedMapper class javadoc explains how to use parameters.
The other option I can think of would be (Note: this is untested):
class MyPageParametersEncoder implements IPageParametersEncoder() {
public PageParameters decodePageParameters(Request request)
{
PageParameters parameters = new PageParameters();
int i = 0;
for (Iterator<String> segment = request.getUrl().getSegements().iterator(); segment.hasNext()) {
String key = segment.next();
String value = segment.next();
parameters.add(key, value);
}
return parameters.isEmpty() ? null : parameters;
}
public Url encodePageParameters(PageParameters pageParameters)
{
Url url = new Url();
for (PageParemeters.NamedPair pair : pageParameters.getAllNamed() {
url.getSegments().add(pair.getKey());
url.getSegments().add(pair.getValue());
}
return url;
}
}
mount(new MountedMapper("/myurl/", MyPage.class, new MyPageParametersEncoder());
No need of custom IPageParametersEncoder.
With mountPage("/myurl/paramName1/${paramValue1}/paramName2/${paramValue2}", MyPage.class) the URL will look like in 1.4 but the values will be reachable as StringValue value1 = parameters.get("paramValue1"). Similar for value2.
With mountPage("/myurl/${paramValue1}/${paramValue2}", MyPage.class) is the same according to extracting the values, just shorter URL will be used.
It also supports optional paramaters - #{optionalValue3}.
NOTE: A new class has been added to Wicket 1.5.2 for backwards compatibility with 1.4 style URL encoding. It's called UrlPathPageParametersEncoder - use that if you're migrating a wicket 1.4 app to 1.5 and you have bookmarkable page links of the style:
www.mysite.com/name1/value1/name2/value2
We had the exact same issue when migrating from 1.4 to 1.5. Any 1.4 app that has been live for a while would likely have a collection of links pointing to it from external sites on the web. You really want the Wicket 1.5 version of your app to be able to handle these existing hybrid links without generating an error.
When migrating to 1.5, without a 1.4 compatible IPageParametersEncoder implementation, you need to include the full parameter specification in every mount if you want to avoid making changes to each individual Page class that reads parameters. The implementation below means that is no longer necessary. Just mount the page as livid suggests above.
I'm submitting this .java file as a patch to the Wicket devs - they may include it in Wicket in the future to make it easy to implement backwards compatible URL parameter encoding for other 1.4 migrators.
I took luniv's sample code above and made a few small changes to get it compiling/working. The following should work as a parameter encoder to provide 1.4.x style parameter encoding in 1.5.
package org.apache.wicket.request.mapper.parameter;
import java.lang.*;
import org.apache.wicket.request.mapper.parameter.IPageParametersEncoder;
import java.util.Iterator;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.mapper.parameter.PageParameters;
public
class HybridPageParametersEncoder implements IPageParametersEncoder
{
/**
* Encodes a URL in the form:
*
* /mountpoint/paramName1/paramValue1/paramName2/paramValue2
*
* (i.e. a URL using the pre wicket 1.5 Hybrid URL strategy)
*/
public Url encodePageParameters(PageParameters pageParameters)
{
Url url = new Url();
for (PageParameters.NamedPair pair : pageParameters.getAllNamed())
{
url.getSegments().add(pair.getKey());
url.getSegments().add(pair.getValue());
}
return url;
}
/**
* Decodes a URL in the form:
*
* /mountpoint/paramName1/paramValue1/paramName2/paramValue2
*
* (i.e. a URL using the pre wicket 1.5 Hybrid URL strategy)
*/
public PageParameters decodePageParameters(Request request)
{
PageParameters parameters = new PageParameters();
int i = 0;
for (Iterator<String> segment = request.getUrl().getSegments().iterator(); segment.hasNext(); )
{
String key = segment.next();
String value = segment.next();
parameters.add(key, value);
}
return parameters.isEmpty() ? null : parameters;
}
}