While trying to run my test for the following code. I am getting some error detailed below.
I observed at the time it makes mkdirs() calls in my code throws IllegalArgumentException when I try to run my test.
MyClass.java
package com.javaeasily.demos.junit;
import java.util.ArrayList;
public class MyClass {
private final NodeHelper nodeHelper;
private static final ArrayList<String> ACTIVE_SERVICES_POST_RECONFIGURE = new ArrayList<>();
// Only allow construction if number is greater than one
MyClass() {
ACTIVE_SERVICES_POST_RECONFIGURE.add("my-node-" + NodeUtils.getMyNode());
nodeHelper = new NodeHelper();
}
public void reconfigureNode() {
if (ACTIVE_SERVICES_POST_RECONFIGURE.isEmpty()) {
return;
}
try {
nodeHelper.createStagingDir();
} catch (Exception e) {
throw new RuntimeException("Cannot reconfigure node");
}
}
}
NodeUtils.java
package com.javaeasily.demos.junit;
import org.apache.commons.lang3.StringUtils;
public class NodeUtils {
private static final String HOSTNAME_PREFIX = "my-node-";
public static String hostnameToNode(String hostname) {
if (!hostname.startsWith(HOSTNAME_PREFIX)) {
throw new IllegalArgumentException(hostname + " is not recognized hostname");
}
return StringUtils.removeStart(hostname, HOSTNAME_PREFIX);
}
public static String getHostname() {
return System.getenv("HOSTNAME");
}
public static String getMyNode() {
return hostnameToNode(getHostname());
}
}
NodeHelper.java
package com.javaeasily.demos.junit;
import java.io.File;
public class NodeHelper {
private static final String STAGING_DIR = "/staging/";
public void createStagingDir() {
File stagingDir = new File(STAGING_DIR);
if (!stagingDir.exists() && !stagingDir.mkdirs()) {
throw new IllegalArgumentException("Could not create staging dir");
}
}
}
MyClassTest.java
package com.javaeasily.demos.junit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
private MyClass myclass;
#BeforeEach
public void SetUp() {
try (MockedStatic<NodeUtils> nodeUtilsMockedStatic = Mockito.mockStatic(NodeUtils.class);) {
nodeUtilsMockedStatic.when(NodeUtils::getMyNode).thenReturn("foo");
myclass = new MyClass();
}
}
#Test
public void testReconfigureNode() {
myclass.reconfigureNode();
}
}
When I try to run my test I get following error:
java.lang.RuntimeException: Cannot reconfigure node
at com.javaeasily.demos.junit.MyClass.reconfigureNode(MyClass.java:22)
Post debugging I see that:
java.lang.IllegalArgumentException: Could not create staging dir
Does anyone care to enlighten me as to what I am doing wrong?
To mock NodeHelper you should not create it inside MyClass, but inject it from the outside via the constructor. i.e.
MyClass(Nodehelper nodeHelper) {
ACTIVE_SERVICES_POST_RECONFIGURE.add("my-node-" + NodeUtils.getMyNode());
this.nodeHelper = nodeHelper;
}
This allows you to create a mocked NodeHelper in your test, pass it to MyClass and set the desired behavior as expectations.
I am getting the error
Cannot invoke "Object.getClass()" because "obj" is null
on the line
m.invoke(null);
Here are the classes:
package device;
public class Conveyor {
private String ID;
public Conveyor(String ID) {this.ID = ID;}
public void Start() {
}
public void Stop() {
}
}
package main;
import java.lang.reflect.Method;
import device.Conveyor;
public class Main {
public static void main(String args[]) {
Conveyor myConveyor = new Conveyor("C1");
Class<Conveyor> conveyorClass = (Class<Conveyor>) myConveyor.getClass();
for (Method m : conveyorClass.getMethods()) {
System.out.println(m.getName());
if (m.getName().equals("Start")) {
try {
m.invoke(null);
} catch (Exception ex) {
System.err.println(ex.getLocalizedMessage());
}
}
}
}
}
Acording to the docs, the invoke method receive the reference of the object that will call the method. So you need to change the code to be:
m.invoke(myConveyor);
See the docs https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-
Per #markspace , I edited the invoke to add the invoking object:
m.invoke(myConveyor);
I am trying to develop a dynamic Factory Class, where Factory Class does not know the factories, I will put the code so that the question is clearer.
App.java:
package br.com.factory;
public class App {
public static void main(String[] args) {
Product product = ProductFactory.createProduct("Xiaomi", "MI XX");
if (product != null) {
System.out.println(product.getName());
}
}
}
Product.java:
package br.com.factory;
public interface Product {
public String getName();
}
ProductFactory.java:
package br.com.factory;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ProductFactory {
private static HashMap<String, Map<String, Supplier<Product>>> registries = new HashMap<>();
private ProductFactory(){}
static {
ClassLoaderInitializer.initialize(ProductSupplier.class);
}
public static Product createProduct(String manufacturer, String model) {
Map<String, Supplier<Product>> manufacturers = registries.getOrDefault(manufacturer, null);
if (manufacturers != null){
Supplier<Product> suppliers = manufacturers.getOrDefault(model, null);
if (suppliers != null) {
return suppliers.get();
}
}
return null;
}
public static void registerFactory(String manufacturer, String model, Supplier<Product> supplier) {
registries
.computeIfAbsent(manufacturer, p -> new HashMap<>())
.putIfAbsent(model, supplier);
}
}
ProductSupplier.java:
package br.com.factory;
import java.util.function.Supplier;
public interface ProductSupplier extends Supplier<Product> {
}
XiaomiFactory.java:
package br.com.factory.xiaomi;
import br.com.factory.Product;
import br.com.factory.ProductFactory;
import br.com.factory.ProductSupplier;
public class XiaomiFactory implements ProductSupplier {
static {
ProductFactory.registerFactory("Xiaomi", "MI XX", XiamoMiXX::new);
}
private XiaomiFactory() {
}
#Override
public Product get() {
return new XiamoMiXX();
}
}
class XiamoMiXX implements Product {
#Override
public String getName() {
return "Xiaomi Mi XX";
}
}
ClassLoaderInitializer.java:
package br.com.factory;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class ClassLoaderInitializer {
private ClassLoaderInitializer() {
}
public static void initialize(Class<?> parentClass) {
try {
Enumeration<URL> resources = ClassLoaderInitializer.class.getClassLoader().getResources("");
while (resources.hasMoreElements()) {
URL nextElement = resources.nextElement();
try (URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { nextElement });) {
URL[] urLs = urlClassLoader.getURLs();
for (URL u : urLs) {
try {
File file = new File(u.toURI());
initializeClass(file, file, urlClassLoader, parentClass);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void initializeClass(File root, File file, ClassLoader loader, Class<?> parentClass) {
if (file.isDirectory()) {
File[] listFiles = file.listFiles();
for (File f : listFiles) {
initializeClass(root, f, loader, parentClass);
}
} else {
if (file.getName().toUpperCase().endsWith(".class".toUpperCase())) {
try {
String fileName = file.toString();
String className = fileName.substring(root.toString().length() + 1,
fileName.toUpperCase().lastIndexOf(".class".toUpperCase())).replace(File.separator, ".");
Class<?> clazz = Class.forName(className, false, loader);
if (clazz.isAssignableFrom(parentClass)) {
Class.forName(className, true, loader);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
The problem occurs in the initializeClass method,
more precisely in:
Class <?> Clazz = Class.forName (className, false, loader);
if (clazz.isAssignableFrom (parentClass)) {
Class.forName (className, true, loader);
}
the idea of the ClassLoaderInitializer class is to initialize the classes that inherit from a given class "Class <?> parentClass"
but when I call the method
Class.forName (className, true, loader);
for the second time, passing true in the second parameter, the class is not initialized.
if I call:
Class.forName (className, true, loader);
directly, the initializeClass method will initialize all classes, what I would not like to happen.
is there any outside of me explicitly initializing (forcing) the class specifies?
Use ServiceLoader
ServiceLoader allows you to register services using metadata in the META-INF/services/ folder. It allows you to use a Java API to register all your services at once. It's better than trying to do it yourself, especially since it's standardized and doesn't require "magic" to get registered. Magic which might be broken in Java 9 and later with the introduction of modules.
This is how you should use it:
ProductSupplier.java
public interface ProductSupplier extends Supplier<Product> {
// Add these methods
String getManufacturer();
String getModel();
}
ProductFactory.java
public class ProductFactory {
private static final Map<List<String>, ProductSupplier> SUPPLIERS;
static {
var suppliers = new HashMap<List<String>, ProductSupplier>();
for (var supplier : ServiceLoader.load(ProductSupplier.class)) { // Yes, it's as easy as this.
var key = List.of(supplier.getManufacturer(), supplier.getModel());
suppliers.put(key, supplier);
}
SUPPLIERS = Map.copyOf(suppliers);
}
public static Product createProduct(String manufacturer, String model) {
var key = List.of(manufacturer, model);
var supplier = suppliers.getOrDefault(key, () -> null);
return supplier.get();
}
}
XiaomiFactory.java
public class XiaomiFactory implements ProductSupplier {
#Override public String getManufacturer() { return "Xiaomi"; }
#Override public String getModel() { return "Mi XX"; }
#Override public Product get() { return new XiaomiMiXX(); }
}
In META-INF/services/com.br.factory.ProductSupplier:
com.br.factory.xiaomi.XiaomiFactory
com.br.factory.samsung.SamsungFactory # Need to create
com.br.factory.apple.AppleFactory # Need to create
As I mentioned in the title. Here is my code below
import java.lang.reflect.Field;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ObjectNode;
public class ReflectClass {
public Double getDoubleKey() {
return doubleKey;
}
public void setDoubleKey(Double doubleKey) {
this.doubleKey = doubleKey;
}
public Long getLongKey() {
return longKey;
}
public void setLongKey(Long longKey) {
this.longKey = longKey;
}
private Double doubleKey;
private Long longKey;
public ReflectClass(JsonNode node) throws IllegalAccessException {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(Double.class)) {
field.setDouble(this, node.get(field.getName()).asDouble());
} else if (field.getType().equals(Long.class)) {
field.setLong(this, node.get(field.getName()).asLong());
}
}
}
public static void main(String[] args) throws IllegalAccessException {
ObjectNode jNode = new ObjectMapper().createObjectNode();
jNode.put("doubleKey", 1.0);
jNode.put("longKey", 11L);
new ReflectClass(jNode);
}
}
However when I run the code above, the error like below pops up.
java.lang.IllegalArgumentException: Can not set java.lang.Double field models.weibo.ReflectClass.doubleKey to (double)1.0
I can certainly initialise the class instance by the traditional way like this.doubleKey = node.get("doubleKey").asDouble(). However if there are too many fields in this class, I'd prefer to initialise it through a loop.
I am new to java technology.So i was going through SequenceInputStreamI tried below code,but i am not able to find the exact problem, kindly some one help
public class SequenceInput {
public static void main(String[] args) throws IOException {
Enumeration e=new MyEnum();
SequenceInputStream sin=new SequenceInputStream(e);
DataInputStream din=new DataInputStream(sin);
String s="";
while(null !=s) {
s=din.readLine();
if(null !=s) {
System.out.println(s);
}
}
din.close();
// new Vector().elements();
}
//Enumeration Class
public class MyEnum implements Enumeration{
InputStream in[];
int i=0;
public MyEnum(){
try {
in=new InputStream[] {new FileInputStream("src/a1.txt"),new FileInputStream("src/a2.txt"),new FileInputStream("src/a3.txt"),new FileInputStream("src/a4.txt")};
}
catch(Exception e) {
}
}
#Override
public boolean hasMoreElements() {
if(in.length<=4) {
return true;
}
else
return false;
}
#Override
public Object nextElement() {
return in[i++];
}
}
}
In this line Enumeration e=new MyEnum(); it is showing
- No enclosing instance of type SequenceInput is accessible. Must qualify the
allocation with an enclosing instance of type SequenceInput (e.g. x.new A() where x is an
instance of SequenceInput).
I am not getting the exact problem.
Else i have used new Vector().add() and it was working fine with sequenceInputStream. Wanted to know about above code..Where am i making mistake.
Thanks in advance.
To access your class without needed an enclosing instance, you have to make it static
public static class MyEnum implements Enumeration {
...
}
Check this
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
public class SequenceInput {
public static void main(String[] args) throws IOException {
Enumeration e=new MyEnum();
SequenceInputStream sin=new SequenceInputStream(e);
DataInputStream din=new DataInputStream(sin);
String s="";
while(null !=s) {
s=din.readLine();
if(null !=s) {
System.out.println(s);
}
}
din.close();
// new Vector().elements();
}
//Enumeration Class
public static class MyEnum implements Enumeration{
InputStream in[];
int i=0;
public MyEnum(){
try {
in=new InputStream[] {new FileInputStream("src/a1.txt"),new FileInputStream("src/a2.txt"),new FileInputStream("src/a3.txt"),new FileInputStream("src/a4.txt")};
}
catch(Exception e) {
}
}
#Override
public boolean hasMoreElements() {
if(in.length<=4) {
return true;
}
else
return false;
}
#Override
public Object nextElement() {
return in[i++];
}
}
}