The output looks good and please don't mid the changed main method. I had to change it to be able to test the code on my windows box. As commented in the modified main the getProtectionDomain() method returns a path with a leading /. And that would result in a windows path similar to /c:\pathToTheClass. When I switch the mains only the second requirement fails. But why. I load the two classes implementing the HiddenClass interface. Even the class with the private constructor. So what is validation complaining about? What am I missing?
package com.codegym.task.task36.task3606;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/*
Mastering ClassLoader and Reflection
*/
public class Solution {
private List<Class> hiddenClasses = new ArrayList<>();
private String packageName;
public Solution(String packageName) {
this.packageName = packageName;
}
/*
public static void main(String[] args) throws ClassNotFoundException {
Solution solution = new Solution(Solution.class.getProtectionDomain().getCodeSource().getLocation().getPath() + "com/codegym/task/task36/task3606/data/second");
solution.scanFileSystem();
System.out.println(solution.getHiddenClassObjectByKey("secondhiddenclassimpl"));
System.out.println(solution.getHiddenClassObjectByKey("firsthiddenclassimpl"));
System.out.println(solution.getHiddenClassObjectByKey("packa"));
}
*/
public static void main(String[] args) throws ClassNotFoundException {
//Solution solution = new Solution(Solution.class.getProtectionDomain().getCodeSource().getLocation().getPath() + "com/codegym/task/task36/task3606/data/second");
// cause of the leading / I use a replaceFirst to be able to test the code on my win box
Solution solution = new Solution(Solution.class.getProtectionDomain().getCodeSource().getLocation().getPath().replaceFirst("/","") + "com/codegym/task/task36/task3606/data/second");
solution.scanFileSystem(); // load all classes into the hiddenClasses instance list
// System.out.println(solution.hiddenClasses);
System.out.println(solution.getHiddenClassObjectByKey("secondhiddenclassimpl"));
System.out.println(solution.getHiddenClassObjectByKey("firsthiddenclassimpl"));
System.out.println(solution.getHiddenClassObjectByKey("packa"));
}
public void scanFileSystem() throws ClassNotFoundException {
try {
hiddenClasses = Files.list(Paths.get(packageName))
.filter(file -> file.toString().endsWith(".class"))
.map(this::loadClass)
.collect(Collectors.toList());
} catch (IOException ioe) {
throw new RuntimeException("Problem reading filesystem", ioe);
}
}
public Class<?> loadClass(Path p) {
try {
return new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] content = Files.readAllBytes(Paths.get(name));
return defineClass(null, content, 0, content.length);
} catch (IOException ioe) {
throw new RuntimeException("Problem reading file into array", ioe);
}
}
}.findClass(p.toString());
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Problem creating class", cnfe);
}
}
public HiddenClass getHiddenClassObjectByKey(String key) {
if (key == null) return null;
// this here is just a little factory that returns an object if key is in the hiddenClasses list
Class clazz = hiddenClasses
.stream()
.filter(e -> e.getSimpleName().equalsIgnoreCase(key))
.findFirst().orElse(null);
if (clazz == null) return null;
try {
// SecondHiddenClass has a private constructor
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return (HiddenClass) constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
return null;
}
}
}