SocketImpl and JUnit/Gradle - java

I'm trying to make Socks v4 work out of the box in java.net, and I seem to have succeeded!
Roughtly the code that I'm using is this:
class SocketImplFactorySocks4 implements SocketImplFactory {
#Override
public SocketImpl createSocketImpl() {
System.out.println("Socket implementation triggered");
try {
return socketSocks4Factory();
} catch (Exception e) {
e.printStackTrace();
throw new Error("Can't go further");
}
}
private SocketImpl socketSocks4Factory() throws
[...] {
Class<?> aClass = Class.forName("java.net.SocksSocketImpl");
Constructor<?> cons = aClass.getDeclaredConstructor();
if (!cons.isAccessible())
cons.setAccessible(true);
Object socket = cons.newInstance();
Method method = socket.getClass().getDeclaredMethod("setV4");
if (!method.isAccessible())
method.setAccessible(true);
method.invoke(socket);
Field field = socket.getClass().getDeclaredField("useV4");
field.setAccessible(true);
Object value = field.get(socket);
return (SocketImpl) socket;
}
}
Long story short, it works when I create a socket and pass -DsocksProxyHost and -DsocksProxyPort.
My problem is when I use the same code in my junit test, I can check with Reflections that Socket.impl.useV4 is set to true, socksProxy* settings are set systemwide, but when I use my socket, it avoids using proxy altogether (I can see it in wireshark).
It's either JUnit or Gradle, but I've reached my limits. Please advice on where should I go next. build.gradle.kts for reference:
tasks{
test{
systemProperty("socksProxyHost", "localhost")
systemProperty("socksProxyPort", "8080")
}
}
Thanks in advance!

Well, it took me way too much time to figure it out, but I did. My initial goal was to test my Socks v4 server code, but there were two problems on my way:
1) Even though Java Socket has support for Socks v4 as client, it is not enabled by default. And there is no way to flip the toggle.
2) Having solved #1, I tried to write E2E test to smoke the whole thing, but for some reason it was avoiding going into the Socks proxy, even though the toggle (useV4) was true. This is what I came with here on SO.
To solve the first problem, I implemented SocketImplFactory (see above in the question).
What helped to tackle the topic question was my admin background, even though it didn't kick in until recently. :) I separated the original suspects (JUnit and Gradle) and made the test in a standalone psvm file. The test didn't work, it still avoided going through the proxy. And this is when it hit me: exception for localhost!
Basically, there is a hardcoded exception for localhost(127.0.0.1, ::, etc) deep in Java core library. After some searching I came across DsocksNonProxyHosts option. Which didn't help, as you might have guessed already :)
Eventually I ended up at this answer, which mentioned that I might need to implement ProxySelector. Which I did:
static class myProxySelector extends ProxySelector {
#Override
public List<Proxy> select(URI uri) {
List<Proxy> proxyl = new ArrayList<Proxy>(1);
InetSocketAddress saddr = InetSocketAddress.createUnresolved("localhost", 1080);
Proxy proxy = SocksProxy.create(saddr, 4);
proxyl.add(proxy);
System.out.println("Selecting Proxy for " + uri.toASCIIString());
return proxyl;
}
#Override
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
if (uri == null || sa == null || ioe == null) {
throw new IllegalArgumentException("Arguments can't be null.");
}
}
}
The whole socket setup looks like this:
private void setupSocket() throws IOException {
Socket.setSocketImplFactory(new SocketImplFactorySocks4());
ProxySelector proxySelector = new myProxySelector();
ProxySelector.setDefault(proxySelector);
proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 1080));
}
Now everything I wanted works: I'm both able to E2E-test my socks4 code and can do it localhost.

Related

Is there a way to have Jedis automatically use a connection pool for command methods?

I came across a class in a project I'm working on that looks like
public class RedisClient {
Logger logger = LoggerFactory.getLogger(RedisClient.class);
JedisPool pool;
public RedisClient(String redisHost, int redisPort, String redisPassword) {
JedisPoolConfig poolConfig = buildPoolConfig();
try {
pool = new JedisPool(poolConfig, redisHost, redisPort, 10000, redisPassword, true);
} catch (Exception e) {
logger.error("There's been an error while Jedis attempted to retrieve a thread from the pool", e);
}
}
public void set(String key, String value) {
try (Jedis jedis = pool.getResource()) {
jedis.set(key, value);
}
}
// ... a few more command methods wrapped with try (Jedis jedis = pool.getResource())
// like get, expire, etc.
While this isn't a bad approach by any means (pretty standard adapter pattern to my eyes), I'm wondering if Jedis either takes care of this automatically (coming from Python, I was hoping it maybe hid this detail a la redis, or there was another way to configure an existing client to "use a connection pool for each command". I see that jedis.Jedis has a setDataSource method that accepts a JedisPool, but I'm having trouble determining what that actually does and if it helps me answer my question.
JedisPool is implemented on top of commons-pool2. According to the design of commons-pool2, you can borrow an object but you'd have to return that object after using it. Jedis incorporates close method in this returning process to ease users' works with the help of Java's try-with-resources feature. setDataSource method is used in the borrowing process, similar to close method being used in returning process.
Last but not least, a user must not use setDataSource method; unless s/he is implementing her own version of JedisPool.

RMI Stubs: Force host-value on client side

We would like to access the same RMI-server from different hosts in our network (dev-pc via ssh-tunnel, jenkins-server via direct connection). The problem is that the RMI-host is known under different names on the different client hosts.
This is not a problem when we connect to the registry, because we can set the target host name like this:
Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());
But when we lookup the remote object like below, it contains the wrong hostname.
HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());
In the debugger I can observe that the host is like needed on the Registry object, but not on the Stub:
We get a connection timeout as soon as we call a method on the Stub. If I manually change the host value to localhost in the debugger the method invocation succeeds.
I'm aware that I can set java.rmi.server.hostname on the server side but then the connection from jenkins does not work anymore.
The simplest solution would be that I force RMI to use the same host as for the registry for all Stubs retrieved from that registry. Is there a better way than replacing the host-value in the Stub via reflection?
Unfortunately RMI has a deeply built-in assumption that the server host has a single 'most public' IP address or hostname. This explains the java.rmi.server.hostname fiasco. If your system doesn't comply you are out of luck.
As pointed out by EJP there seems to be no elegant out-of-the-box solution.
I can think of two unelegant ones:
Changing the network configuration on every client host in order to redirect traffic to the non-reachable ip to localhost instead.
Changing the host value on the "hello"-object via Reflection.
I went for the second option because I'm in a test environment and the code in question will not go productive anyways. I wouldn't recommend to do this otherwise, because this code might break with future versions of java and won't work if a Security Manager is in place.
However, here my working code:
private static void forceRegistryHostNameOnStub(Object registry, Object stub) {
try {
String regHost = getReferenceToInnerObject(registry, "ref", "ref", "ep", "host").toString();
Object stubEp = getReferenceToInnerObject(stub, "h", "ref", "ref", "ep");
Field fStubHost = getInheritedPrivateField(stubEp, "host");
fStubHost.setAccessible(true);
fStubHost.set(stubEp, regHost);
} catch (Exception e) {
LOG.error("Applying the registry host to the Stub failed.", e);
}
}
private static Object getReferenceToInnerObject(Object from, String... objectHierarchy) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Object ref = from;
for (String fieldname : objectHierarchy) {
Field f = getInheritedPrivateField(ref, fieldname);
f.setAccessible(true);
ref = f.get(ref);
}
return ref;
}
private static Field getInheritedPrivateField(Object from, String fieldname) throws NoSuchFieldException {
Class<?> i = from.getClass();
while (i != null && i != Object.class) {
try {
return i.getDeclaredField(fieldname);
} catch (NoSuchFieldException e) {
// ignore
}
i = i.getSuperclass();
}
return from.getClass().getDeclaredField(fieldname);
}
The method invocation on the Stub succeeds now:
Registry registry = LocateRegistry.getRegistry("hostname", 10099, new CustomSslRMIClientSocketFactory());
HelloRemote hello = (HelloRemote) registry.lookup(HelloRemote.class.getSimpleName());
forceRegistryHostNameOnStub(registry, hello); // manipulate the stub
hello.doSomething(); // succeeds

Change minecraft proxy in mod [Java]

Quite a while ago I posted a question about how to use a proxy in minecraft.
To sum most of the question up, I'm working on a mob in which I would like to be able to change the proxy of the JVM whenever I want during the code. I understand that it is easy to just set the proxy when invoking the VM but I want to be able to change it in real-time while in the game so that when I connect to a server my IP is different (this mod is mostly going to be used by people who are parranoid of joining servers and people knowing their IP.
I have tried setting System Properties as so:
System.setProperty("http.proxyHost", "186.116.8.170");
System.setProperty("http.proxyPort", "8080");
and also tried doing
System.setProperty("https.proxyHost", "186.116.8.170");
System.setProperty("https.proxyPort", "8080");
but none of that worked.
I would appreciate any help given, thanks.
minecraft used netty, you need to use mixin to modify net.minecraft.network.NetworkManager
code for 1.8.9 below
#Mixin(NetworkManager.class)
public abstract class MixinNetworkManager {
#Shadow
#Final
public static LazyLoadBase<NioEventLoopGroup> CLIENT_NIO_EVENTLOOP;
#Overwrite
public static NetworkManager createNetworkManagerAndConnect(InetAddress address, int serverPort, boolean useNativeTransport) {
final NetworkManager networkmanager = new NetworkManager(EnumPacketDirection.CLIENTBOUND);
Bootstrap bootstrap=new Bootstrap();
EventLoopGroup eventLoopGroup;
Proxy proxy=ProxyManager.INSTANCE.getProxy();
if(proxy.type().equals(Proxy.Type.DIRECT)){
eventLoopGroup=CLIENT_NIO_EVENTLOOP.getValue();
bootstrap.channel(NioSocketChannel.class);
}else {
if(!Epoll.isAvailable()||!useNativeTransport){
System.out.println("Something goes wrong! Maybe you can disable proxy. [Epoll="+Epoll.isAvailable()+", UNT="+useNativeTransport+"]");
}
eventLoopGroup=new OioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
bootstrap.channelFactory(new ProxyOioChannelFactory(proxy));
}
bootstrap.group(eventLoopGroup).handler(new ChannelInitializer<Channel>() {
protected void initChannel(Channel channel) {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException var3) {
var3.printStackTrace();
}
channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("splitter", new MessageDeserializer2()).addLast("decoder", new MessageDeserializer(EnumPacketDirection.CLIENTBOUND)).addLast("prepender", new MessageSerializer2()).addLast("encoder", new MessageSerializer(EnumPacketDirection.SERVERBOUND)).addLast("packet_handler", networkmanager);
}
});
bootstrap.connect(address, serverPort).syncUninterruptibly();
return networkmanager;
}

Secure Nashorn JS Execution

How can I securely execute some user supplied JS code using Java8 Nashorn?
The script extends some computations for some servlet based reports. The app has many different (untrusted) users. The scripts should only be able to access a Java Object and those returned by the defined members. By default the scripts could instantiate any class using Class.forName() (using .getClass() of my supplied object). Is there any way to prohibit access to any java class not explicitly specified by me?
I asked this question on the Nashorn mailing list a while back:
Are there any recommendations for the best way to
restrict the classes that Nashorn scripts can create to a whitelist?
Or is the approach the same as any JSR223 engine (custom classloader
on the ScriptEngineManager constructor)?
And got this answer from one of the Nashorn devs:
Hi,
Nashorn already filters classes - only public classes of non-sensitive packages (packages listed in package.access security
property aka 'sensitive'). Package access check is done from a
no-permissions context. i.e., whatever package that can be accessed
from a no-permissions class are only allowed.
Nashorn filters Java reflective and jsr292 access - unless script has RuntimePermission("nashorn.JavaReflection"), the script wont be
able to do reflection.
The above two require running with SecurityManager enabled. Under no security manager, the above filtering won't apply.
You could remove global Java.type function and Packages object (+ com,edu,java,javafx,javax,org,JavaImporter) in global scope and/or
replace those with whatever filtering functions that you implement.
Because, these are the only entry points to Java access from script,
customizing these functions => filtering Java access from scripts.
There is an undocumented option (right now used only to run test262 tests) "--no-java" of nashorn shell that does the above for you. i.e.,
Nashorn won't initialize Java hooks in global scope.
JSR223 does not provide any standards based hook to pass a custom class loader. This may have to be addressed in a (possible) future
update of jsr223.
Hope this helps,
-Sundar
Added in 1.8u40, you can use the ClassFilter to restrict what classes the engine can use.
Here is an example from the Oracle documentation:
import javax.script.ScriptEngine;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
public class MyClassFilterTest {
class MyCF implements ClassFilter {
#Override
public boolean exposeToScripts(String s) {
if (s.compareTo("java.io.File") == 0) return false;
return true;
}
}
public void testClassFilter() {
final String script =
"print(java.lang.System.getProperty(\"java.home\"));" +
"print(\"Create file variable\");" +
"var File = Java.type(\"java.io.File\");";
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(
new MyClassFilterTest.MyCF());
try {
engine.eval(script);
} catch (Exception e) {
System.out.println("Exception caught: " + e.toString());
}
}
public static void main(String[] args) {
MyClassFilterTest myApp = new MyClassFilterTest();
myApp.testClassFilter();
}
}
This example prints the following:
C:\Java\jre8
Create file variable
Exception caught: java.lang.RuntimeException: java.lang.ClassNotFoundException:
java.io.File
I've researched ways of allowing users to write a simple script in a sandbox that is allowed access to some basic objects provided by my application (in the same way Google Apps Script works). My conclusion was that this is easier/better documented with Rhino than with Nashorn. You can:
Define a class-shutter to avoid access to other classes: http://codeutopia.net/blog/2009/01/02/sandboxing-rhino-in-java/
Limit the number of instructions to avoid endess-loops with observeInstructionCount: http://www-archive.mozilla.org/rhino/apidocs/org/mozilla/javascript/ContextFactory.html
However be warned that with untrusted users this is not enough, because they can still (by accident or on purpose) allocate a hugh amount of memory, causing your JVM to throw an OutOfMemoryError. I have not found a safe solution to this last point yet.
You can quite easily create a ClassFilter which allows fine-grained control of which Java classes are available in JavaScript.
Following the example from the Oracle Nashorn Docs:
class MyCF implements ClassFilter {
#Override
public boolean exposeToScripts(String s) {
if (s.compareTo("java.io.File") == 0) return false;
return true;
}
}
I have wrapped this an a few other measures in a small library today: Nashorn Sandbox (on GitHub). Enjoy!
So far as I can tell, you can't sandbox Nashorn. An untrusted user can execute the "Additional Nashorn Built-In Functions" listed here:
https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/shell.html
which include "quit()". I tested it; it exits the JVM entirely.
(As an aside, in my setup the global objects, $ENV, $ARG, did not work, which is good.)
If I'm wrong about this, someone please leave a comment.
The best way to secure a JS execution in Nashorn is to enable the SecurityManager and let Nashorn deny the critical operations.
In addition you can create a monitoring class that check the script execution time and memory in order to avoid infinite loops and outOfMemory.
In case you run it in a restricted environment without possibility to setup the SecurityManager, you can think to use the Nashorn ClassFilter to deny all/partial access to the Java classes. In addition to that you must overwrite all the critical JS functions (like quit() etc.).
Have a look at this function that manage all this aspects (except memory management):
public static Object javascriptSafeEval(HashMap<String, Object> parameters, String algorithm, boolean enableSecurityManager, boolean disableCriticalJSFunctions, boolean disableLoadJSFunctions, boolean defaultDenyJavaClasses, List<String> javaClassesExceptionList, int maxAllowedExecTimeInSeconds) throws Exception {
System.setProperty("java.net.useSystemProxies", "true");
Policy originalPolicy = null;
if(enableSecurityManager) {
ProtectionDomain currentProtectionDomain = this.getClass().getProtectionDomain();
originalPolicy = Policy.getPolicy();
final Policy orinalPolicyFinal = originalPolicy;
Policy.setPolicy(new Policy() {
#Override
public boolean implies(ProtectionDomain domain, Permission permission) {
if(domain.equals(currentProtectionDomain))
return true;
return orinalPolicyFinal.implies(domain, permission);
}
});
}
try {
SecurityManager originalSecurityManager = null;
if(enableSecurityManager) {
originalSecurityManager = System.getSecurityManager();
System.setSecurityManager(new SecurityManager() {
//allow only the opening of a socket connection (required by the JS function load())
#Override
public void checkConnect(String host, int port, Object context) {}
#Override
public void checkConnect(String host, int port) {}
});
}
try {
ScriptEngine engineReflex = null;
try{
Class<?> nashornScriptEngineFactoryClass = Class.forName("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
Class<?> classFilterClass = Class.forName("jdk.nashorn.api.scripting.ClassFilter");
engineReflex = (ScriptEngine)nashornScriptEngineFactoryClass.getDeclaredMethod("getScriptEngine", new Class[]{Class.forName("jdk.nashorn.api.scripting.ClassFilter")}).invoke(nashornScriptEngineFactoryClass.newInstance(), Proxy.newProxyInstance(classFilterClass.getClassLoader(), new Class[]{classFilterClass}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("exposeToScripts")) {
if(javaClassesExceptionList != null && javaClassesExceptionList.contains(args[0]))
return defaultDenyJavaClasses;
return !defaultDenyJavaClasses;
}
throw new RuntimeException("no method found");
}
}));
/*
engine = new jdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine(new jdk.nashorn.api.scripting.ClassFilter() {
#Override
public boolean exposeToScripts(String arg0) {
...
}
});
*/
}catch(Exception ex) {
throw new Exception("Impossible to initialize the Nashorn Engine: " + ex.getMessage());
}
final ScriptEngine engine = engineReflex;
if(parameters != null)
for(Entry<String, Object> entry : parameters.entrySet())
engine.put(entry.getKey(), entry.getValue());
if(disableCriticalJSFunctions)
engine.eval("quit=function(){throw 'quit() not allowed';};exit=function(){throw 'exit() not allowed';};print=function(){throw 'print() not allowed';};echo=function(){throw 'echo() not allowed';};readFully=function(){throw 'readFully() not allowed';};readLine=function(){throw 'readLine() not allowed';};$ARG=null;$ENV=null;$EXEC=null;$OPTIONS=null;$OUT=null;$ERR=null;$EXIT=null;");
if(disableLoadJSFunctions)
engine.eval("load=function(){throw 'load() not allowed';};loadWithNewGlobal=function(){throw 'loadWithNewGlobal() not allowed';};");
//nashorn-polyfill.js
engine.eval("var global=this;var window=this;var process={env:{}};var console={};console.debug=print;console.log=print;console.warn=print;console.error=print;");
class ScriptMonitor{
public Object scriptResult = null;
private boolean stop = false;
Object lock = new Object();
#SuppressWarnings("deprecation")
public void startAndWait(Thread threadToMonitor, int secondsToWait) {
threadToMonitor.start();
synchronized (lock) {
if(!stop) {
try {
if(secondsToWait<1)
lock.wait();
else
lock.wait(1000*secondsToWait);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
if(!stop) {
threadToMonitor.interrupt();
threadToMonitor.stop();
throw new RuntimeException("Javascript forced to termination: Execution time bigger then " + secondsToWait + " seconds");
}
}
public void stop() {
synchronized (lock) {
stop = true;
lock.notifyAll();
}
}
}
final ScriptMonitor scriptMonitor = new ScriptMonitor();
scriptMonitor.startAndWait(new Thread(new Runnable() {
#Override
public void run() {
try {
scriptMonitor.scriptResult = engine.eval(algorithm);
} catch (ScriptException e) {
throw new RuntimeException(e);
} finally {
scriptMonitor.stop();
}
}
}), maxAllowedExecTimeInSeconds);
Object ret = scriptMonitor.scriptResult;
return ret;
} finally {
if(enableSecurityManager)
System.setSecurityManager(originalSecurityManager);
}
} finally {
if(enableSecurityManager)
Policy.setPolicy(originalPolicy);
}
}
The function currently use the deprecated Thread stop(). An improvement can be execute the JS not in a Thread but in a separate Process.
PS: here Nashorn is loaded through reflexion but the equivalent Java code is also provided in the comments
I'd say overriding the supplied class's classloader is easiest way to control access to classes.
(Disclaimer: I'm not really familiar with newer Java, so this answer may be old-school/obsolete)
An external sandbox library can be used if you don't want to implement your own ClassLoader & SecurityManager (that's the only way of sandboxing for now).
I've tried "The Java Sandbox" (http://blog.datenwerke.net/p/the-java-sandbox.html) although it's a bit rough around the edges, but it works.
Without the use of Security Manager it is not possible to securely execute JavaScript on Nashorn.
In all releases of Oracle Hotspot that included Nashorn one can write JavaScript that will execute any Java/JavaScript code on this JVM.
As of January 2019, Oracle Security Team insist that use of Security Manager is mandatory.
One of the problems is already discussed in https://github.com/javadelight/delight-nashorn-sandbox/issues/73

How to mock a javax.mail.Session

i need to mock a javax.mail.Session object in my unit tests.
The class javax.mail.Session is marked final so Mockito is not able to create a mock. Does anyone have an idea how to fix this?
Edit:
My test is an Arquillian test and has already an annotation #RunWith(Arquillian.class). Therefore powermock is not an option.
You may refactor your code a little bit. In the "Working with Legacy Code" book by Martin Fowler, he describes a technique of separating the external API (think Java Mail API) from your own application code. The technique is called "Wrap and Skin" and is pretty simple.
What you should do is simply:
Create an interface MySession (which is your own stuff) by extracting methods from the javax.mail.Session class.
Implement that interface by creating a concrete class (looking a bit like the original javax.mail.Session)
In each method DELEGATE the call to equivalent javax.mail.Session method
Create your mock class which implements MySession :-)
Update your production code to use MySession instead of javax.mail.Session
Happy testing!
EDIT: Also take a look at this blog post: http://www.mhaller.de/archives/18-How-to-mock-a-thirdparty-final-class.html
Use PowerMockito to mock it.
#RunWith(PowerMockRunner.class)
// We prepare PartialMockClass for test because it's final or we need to mock private or static methods
#PrepareForTest(javax.mail.Session.class)
public class YourTestCase {
#Test
public void test() throws Exception {
PowerMockito.doReturn(value).when(classUnderTest, "methodToMock", "parameter1");
}
}
You can use the Mock JavaMail project. I first found it from Kohsuke Kawaguchi. Alan Franzoni also has a primer for it on his blog.
When you put this jar file in your classpath, it substitutes any sending of mail to in memory mailboxes that can be checked immediately. It's super easy to use.
Adding this to your classpath is admittedly a pretty heavy handed way to mock something, but you rarely want to send real emails in your automated tests anyway.
If you want to mock a final classes you can use the JDave unfinalizer which can be found here : http://jdave.org/documentation.html#mocking
It uses CGLib to alter the bytecode dynamically when the JVM is loaded to transform the class as a non final class.
This library can then be used with JMock2 ( http://www.jmock.org/mocking-classes.html ) to make your tests because as far as I know, Mockito is not compatible with JDave.
Use Java 8 Functions!
public class SendEmailGood {
private final Supplier<Message> messageSupplier;
private final Consumer<Message> messageSender;
public SendEmailGood(Supplier<Message> messageSupplier,
Consumer<Message> messageSender) {
this.messageSupplier = messageSupplier;
this.messageSender = messageSender;
}
public void send(String[] addresses, String from,
String subject, String body)
throws MessagingException {
Message message = messageSupplier.get();
for (String address : addresses) {
message.addRecipient
(Message.RecipientType.TO, new InternetAddress(address));
}
message.addFrom(new InternetAddress[]{new InternetAddress(from)});
message.setSubject(subject);
message.setText(body);
messageSender.accept(message);
}
}
Then your test code will look something like the following:
#Test
public void sendBasicEmail() throws MessagingException {
final boolean[] messageCalled = {false};
Consumer<Message> consumer = message -> {
messageCalled[0] = true;
};
Message message = mock(Message.class);
Supplier<Message> supplier = () -> message;
SendEmailGood sendEmailGood = new SendEmailGood(supplier, consumer);
String[] addresses = new String[2];
addresses[0] = "foo#foo.com";
addresses[1] = "boo#boo.com";
String from = "baz#baz.com";
String subject = "Test Email";
String body = "This is a sample email from us!";
sendEmailGood.send(addresses, from, subject, body);
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("foo#foo.com"));
verify(message).addRecipient(Message.RecipientType.TO, new InternetAddress("boo#boo.com"));
verify(message).addFrom(new InternetAddress[]{new InternetAddress("baz#baz.com")});
verify(message).setSubject(subject);
verify(message).setText(body);
assertThat(messageCalled[0]).isTrue();
}
To create an integration test, plugin the real Session, and Transport.
Consumer<Message> consumer = message -> {
try {
Transport.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
};
Supplier<Message> supplier = () -> {
Properties properties = new Properties();
return new MimeMessage(Session.getDefaultInstance(properties));
};
See the PowerMock docs for running under JUnit 3, since it did not have runners, or use a byte-code manipulation tool.
If you can introduce Spring into your project you can use the JavaMailSender and mock that. I don't know how complicated your requirements are.
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
#Test
public void createAndSendBookChangesMail() {
// Your custom service layer object to test
MailServiceImpl service = new MailServiceImpl();
// MOCK BEHAVIOUR
JavaMailSender mailSender = mock(JavaMailSender.class);
service.setMailSender(mailSender);
// PERFORM TEST
service.createAndSendMyMail("some mail message content");
// ASSERT
verify(mailSender).send(any(SimpleMailMessage.class));
}
This is a pretty old question, but you could always implement your own Transport using the JavaMail API. With your own transport, you could just configure it according to this documentation. One benefit of this approach is you could do anything you want with these messages. Perhaps you would store them in a hash/set that you could then ensure they actually got sent in your unit tests. This way, you don't have to mock the final object, you just implement your own.
I use the mock-javamail library. It just replace the origin javamail implementation in the classpath. You can send mails normally, it just sends to in-memory MailBox, not a real mailbox.
Finally, you can use the MailBox object to assert anything you want to check.

Categories

Resources