HTTPUrlConnection.setContentHandlerFactory()method throws Exception saying factory is already defined. I understand that. Is it possible to unset the factory and change the contenthandler factory?
The factory field in URLConnection (the superclass of HttpURLConnection) is a static package access member variable. The only place it's modified via the API is the setContentHandlerFactory() method, and as you've noted you can only call it once for any URL connection (or subclass) in the application.
I believe there is a way around it, but it's hardly ideal: You can reset and/or change the value of the factory field using reflection (assuming your application has appropriate security manager privileges to make the field accessible).
Here's a snippet that will do so:
ContentHandlerFactory newFactory = ... // create factory instance
factoryField = URLConnection.class.getDeclaredField( "factory" );
factoryField.setAccessible( true );
factoryField.set( null, newFactory );
The problem with this is that the API doesn't expect this will ever happen, so there may be unwanted side effects (since it applies to all URL connection subclasses). Basically you would be doing it at your own risk.
Related
I'm trying to register a custom URLStreamHandler to handle requests to Amazon S3 URLs in a generic fashion. The implementation of the handler looks pretty much like S3-URLStreamHandler (github). I did not put my Handler class into the sun.net.www.protocol.s3 package, but used the custom package com.github.dpr.protocol.s3. To make Java pickup this package, I provided the system property -Djava.protocol.handler.pkgs="com.github.dpr.protocol" following the documentation of the URL class. However, if I try to process a s3-URL like s3://my.bucket/some-awesome-file.txt, I get a MalformedURLException:
Caused by: java.net.MalformedURLException: unknown protocol: s3
at java.net.URL.<init>(URL.java:600)
at java.net.URL.<init>(URL.java:490)
at java.net.URL.<init>(URL.java:439)
at java.net.URI.toURL(URI.java:1089)
...
My application is a Spring based web application, that currently runs in tomcat, but should not be cluttered with any knowledge about the underlying application container.
I already debugged the respective code and found that my URLStreamHandler can't be initialized as the classloader used to load the class doesn't know it. This is the respective code from java.net.URL (jdk 1.8.0_92):
1174: try {
1175: cls = Class.forName(clsName);
1176: } catch (ClassNotFoundException e) {
1177: ClassLoader cl = ClassLoader.getSystemClassLoader();
1178: if (cl != null) {
1179: cls = cl.loadClass(clsName);
1180: }
1181: }
The class loader of the java.net.URL class (used by Class.forName) is null indicating the bootstrap class loader and the system class loader doesn't know my class. If I put a break point in there and try to load my handler class using the current thread's class loader, it works fine. That is my class is apparently there and is in the application's class path, but Java uses the "wrong" class loaders to lookup the handler.
I'm aware of this question on SOF, but I must not register a custom URLStreamHandlerFactory as tomcat registers it's own factory (org.apache.naming.resources.DirContextURLStreamHandlerFactory) on application start and there must only be one factory registered for a single JVM. Tomcat's DirContextURLStreamHandlerFactory allows to add user factories, that could be used to handle custom protocols, but I don't want to add a dependency to Tomcat in my application code, as the application should run in different containers.
Is there any way to register a handler for custom URLs in a container independent fashion?
UPDATE 2017-01-25:
I gave the different options #Nicolas Filotto proposed a try:
Option 1 - Set custom URLStreamHandlerFactory using reflection
This option works as expected. But by using reflection it introduces a tight dependency to the inner workings of the java.net.URL class. Luckily Oracle is not too eager with fixing convenience issues in the basic java classes - actually this related bug report is open for almost 14 years (great work Sun/Oracle) - and this can easily be unit tested.
Option 2 - Put handler JAR into {JAVA_HOME}/jre/lib/ext
This option works as well. But only adding the handler jar as system extension won't do the trick - of course. One will need to add all dependencies of the handler as well. As these classes are visible to all applications using this Java installation, this may lead to undesired effects due to different versions of the same library in the classpath.
Option 3 - Put handler JAR into {CATALINA_HOME}/lib
This doesn't work. According to Tomcat's classloader documentation resources put into this directory will be loaded using Tomcat's Common classloader. This classloader will not be used by java.net.URL to lookup the protocol handler.
Given these options I'll stay with the reflection variant. All of the options are not very nice, but at least the first one can easily be tested and does not require any deployment logic. However I adapted the code slightly to use the same lock object for synchronization as java.net.URL does:
public static void registerFactory() throws Exception {
final Field factoryField = URL.class.getDeclaredField("factory");
factoryField.setAccessible(true);
final Field lockField = URL.class.getDeclaredField("streamHandlerLock");
lockField.setAccessible(true);
// use same lock as in java.net.URL.setURLStreamHandlerFactory
synchronized (lockField.get(null)) {
final URLStreamHandlerFactory urlStreamHandlerFactory = (URLStreamHandlerFactory) factoryField.get(null);
// Reset the value to prevent Error due to a factory already defined
factoryField.set(null, null);
URL.setURLStreamHandlerFactory(new AmazonS3UrlStreamHandlerFactory(urlStreamHandlerFactory));
}
}
You could either:
1. Use a decorator
One way to set your custom URLStreamHandlerFactory, could be to use a decorator of type URLStreamHandlerFactory in order to wrap the URLStreamHandlerFactory that may have already been defined (by tomcat in this case). The tricky part is the fact that you need to use reflection (which is quite hacky) to get and reset the current factory potentially defined.
Here is the pseudo-code of your decorator:
public class S3URLStreamHandlerFactory implements URLStreamHandlerFactory {
// The wrapped URLStreamHandlerFactory's instance
private final Optional<URLStreamHandlerFactory> delegate;
/**
* Used in case there is no existing URLStreamHandlerFactory defined
*/
public S3URLStreamHandlerFactory() {
this(null);
}
/**
* Used in case there is an existing URLStreamHandlerFactory defined
*/
public S3URLStreamHandlerFactory(final URLStreamHandlerFactory delegate) {
this.delegate = Optional.ofNullable(delegate);
}
#Override
public URLStreamHandler createURLStreamHandler(final String protocol) {
if ("s3".equals(protocol)) {
return // my S3 URLStreamHandler;
}
// It is not the s3 protocol so we delegate it to the wrapped
// URLStreamHandlerFactory
return delegate.map(factory -> factory.createURLStreamHandler(protocol))
.orElse(null);
}
}
Here is the code to define it:
// Retrieve the field "factory" of the class URL that store the
// URLStreamHandlerFactory used
Field factoryField = URL.class.getDeclaredField("factory");
// It is a package protected field so we need to make it accessible
factoryField.setAccessible(true);
// Get the current value
URLStreamHandlerFactory urlStreamHandlerFactory
= (URLStreamHandlerFactory) factoryField.get(null);
if (urlStreamHandlerFactory == null) {
// No factory has been defined so far so we set the custom one
URL.setURLStreamHandlerFactory(new S3URLStreamHandlerFactory());
} else {
// Retrieve the field "streamHandlerLock" of the class URL that
// is the lock used to synchronize access to the protocol handlers
Field lockField = URL.class.getDeclaredField("streamHandlerLock");
// It is a private field so we need to make it accessible
lockField.setAccessible(true);
// Use the same lock to reset the factory
synchronized (lockField.get(null)) {
// Reset the value to prevent Error due to a factory already defined
factoryField.set(null, null);
// Set our custom factory and wrap the current one into it
URL.setURLStreamHandlerFactory(
new S3URLStreamHandlerFactory(urlStreamHandlerFactory)
);
}
}
NB: Starting for Java 9, you will need to add --add-opens java.base/java.net=myModuleName to your launch command to allow deep reflection on the package java.net that includes the class URL from your module myModuleName otherwise calling setAccessible(true) will raise a RuntimeException.
2. Deploy it as an extension
Another way that should avoid this ClassLoder issue could be to move your custom URLStreamHandler into a dedicated jar and deploy it in ${JAVA-HOME}/jre/lib/ext as an installed extension (with all its dependencies) such that it will be available in the Extension ClassLoader, this way your class will be defined in a ClassLoader high enough in the hierarchy to be seen.
As of Java 9 you can specify a URL stream handler provider as described in the URL JavaDoc.
Steps:
create a file resources/META-INF/services/java.net.spi.URLStreamHandlerProvider
content: my.S3URLStreamHandlerProvider
implement URLStreamHandler or copy the S3URLStreamHandler you mentioned to my.S3URLStreamHandler
implement a my.S3URLStreamHandlerProvider and let the only to implement method URLStreamHandlerFactory.createURLStreamHandler return a my.S3URLStreamHandler if protocol.equals("s3")
That should be it. The first time you instantiate a s3-URL your URL stream handler provided will be automatically discovered.
If you mind Kotlin code, you can find a running example for data: URIs here:
java.net.spi.URLStreamHandlerProvider
DataURLStreamHandlerProvider.kt
DataURLStreamHandler.kt
I am using EasyMock and need to set expectations on a method that returns an SqlRowSet. Below is a snippet of code.
// SqlRowSet rowSet = new SqlRowSet(); <-- NOT SURE HOW TO MANUALLY CREATE THIS?
expect(myDao.getCustomerData("All_CUSTOMERS")).andReturn(rowSet);
Anybody know how to manually create an SqlRowSet?
For these types of scenario (i.e. creating a concrete instance of a class is difficult), I typically create a mocked instance. Such as:
SqlRowSet mockedSqlRowSet = createMock(SqlRowSet.class);
How can I get an already existing object spring managed? I would like to hook it up to Springs AoP capabilities using aspectj. I know this to be a challenge since Spring AoP uses dynamic proxies which probably are created along with the object.
Why do I need this?
I have a third-party class which takes a constructor argument which is only known in runtime,
hence it seems I cannot add it to my applicationContext or use springs FactoryBean interface for construction. Is there any other way?
I've already tried the following without great success:
Obj obj = new ThirdPartyObj("runtime constructor arg");
appContext.getAutowireCapableBeanFactory().initializeBean(obj, "Obj");
It might be spring-managed, but I still cannot use it to trigger aspects.
[EDIT] axtavt pointed out the problem is that I don't use the object returned from initializeBean(..). Both mentioned approaches work, but only if:
Using interface ObjInterface obj = (ObjInterface) ac.getBean("obj", args); or we will get a:
java.lang.ClassCastException: $Proxy28 cannot be cast to com.company.Obj
Not using interface but enable CGLIB. This requires a non-private default constructor, or we will get a:
java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
Why not create a new class that wraps the functionality of ThirdPartyObj, and make that Spring-managed. Dependencies can then be injected into its fields and method parameters, and passed on the the instantiated ThirdPartyObj.
You should be able to trigger aspects using this (note that you need to use returned object which can be a proxy):
Obj obj = new ThirdPartyObj("runtime constructor arg");
obj = appContext.getAutowireCapableBeanFactory().initializeBean(obj, "Obj");
Another option is to declare it as a regular bean and pass the constructor argument via getBean():
Obj obj = appContext.getBean("Obj", "runtime constructor arg");
How about annotating the domain object with #Configurable annotation? I myself haven't tried it but seems like it might helpful in your scenario. AspectJ and Spring would create a managed object with attributes defined in the bean. The then created object instance can be used.
What is the difference between Session.getDefaultInstance(props, authenticator) and getInstance(props, authenticator)? In general, when will you choose one over the other?
I also read Java doc on getDefaultInstance(props, authenticator), but still couldn't able to make out the difference distinctly/clearly.
Hope experts can help me in understanding this better.
UPDATE: Actual reason that triggered to ask this question is: We've used Session.getDefaultInstance() method in some places within our web-based application. Sometimes, it throws java.lang.SecurityException: Access to default session denied, on quick googling, it suggested to use Session.getInstance() method instead. Hence, when one would choose one over the other?
If you read the documentation, you will see that
getDefaultInstance
Get the default Session object. If a default has not yet been setup, a new Session object is created and installed as the default.
Therefore, if one does not already exist, it call getInstance()
getInstance
Get a new Session object.
So, a new session object is created, regardless of whether one already exists.
FAQ says: https://javaee.github.io/javamail/FAQ#getdefaultinstance
Q: When should I use Session.getDefaultInstance and when should I
use Session.getInstance?
A: Almost all code should use Session.getInstance. The
Session.getDefaultInstance method creates a new Session the first
time it's called, using the Properties that are passed. Subsequent
calls will return that original Session and ignore any Properties you
pass in. If you want to create different Sessions with different
properties, Session.getDefaultInstance won't do that. If some other
code in the same JVM (e.g., in the same app server) has already
created the default Session with their properties, you may end up
using their Session and your properties will be ignored. This often
explains why your property settings seem to be ignored. Always use
Session.getInstance to avoid this problem.
Cause
This error is raised in the getDefaultInstance method in javax.mail.Session.java. According to this source code, this error occures when the default session object is already initialized, but authenticator instance is renewed or changed, or the class loader of the default session object is different from the argument authentificator's. Maybe the java source code using the default session instance of the java mail is recompiled and reloaded, or duplicate javamail class libraries are included into the Classpath of the environment.
it gives proper solution
javax.mail.Session.java file
public static synchronized Session getDefaultInstance(Properties props,
Authenticator authenticator) {
if (defaultSession == null)
defaultSession = new Session(props, authenticator);
else {
// have to check whether caller is allowed to see default session
if (defaultSession.authenticator == authenticator)
; // either same object or both null, either way OK
else if (defaultSession.authenticator != null &&
authenticator != null &&
defaultSession.authenticator.getClass().getClassLoader() ==
authenticator.getClass().getClassLoader())
; // both objects came from the same class loader, OK
else
// anything else is not allowed
throw new SecurityException("Access to default session denied");
}
return defaultSession;
}
For me, it was very important to use getInstance() instead of getDefaultInstance().
Because after mail session properties was changed, mail session still was storing old properties.
So getDefaultInstance() - it is looks like Singleton.
As docs said:
Note also that the Properties object is used only the first time this method is called, when a new Session object is created. Subsequent calls return the Session object that was created by the first call, and ignore the passed Properties object. Use the getInstance method to get a new Session object every time the method is called.
final RuntimeMXBean remoteRuntime =
ManagementFactory.newPlatformMXBeanProxy(
serverConnection,
ManagementFactory.RUNTIME_MXBEAN_NAME,
RuntimeMXBean.class);
Where the serverConnection is just basically connecting to a jmx server.
What basically is going on is, this piece of code works fine. Let me explain:
The first call of this piece of code calls to server A, I then scrape some data in it and store it into an xml file. Using this information, start up a new server B.
Then, in wanting to verify B, I want to scrape B to compare the metadata. But when I run it I get the exception
java.lang.IllegalArgumentException: java.lang:type=Runtime is not an instance of interface java.lang.management.RuntimeMXBean
at java.lang.management.ManagementFactory.newPlatformMXBeanProxy(ManagementFactory.java:617
)
But, not sure what changes here since the parameters that are giving me problems are managed by the ManagementFactory class I don't have control over.
The problem was with my own MBeanServer implementation.
I had it returning false for the isInstanceOf() method if the passed in objectName returned a null Object. It turns out that this happened at all RunTime Classes so after reading http://tim.oreilly.com/pub/a/onjava/2005/01/26/classloading.html under the Class Loader section, I went with the fact that my ClassLoaderImplementation was incorrect and was loading these incorrectly.
Work around was just to return true in isInstanceOf() for these RunTime classes.