Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/main/java/mate.academy/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
public class Main {

public static void main(String[] args) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The custom @Component annotation needs to live in the same package that the rest of the codebase imports (mate.academy.lib); make sure you are not accidentally maintaining duplicate Component definitions in different packages, as this can cause runtime confusion and violate the requirement to use only one @Component annotation (checklist item #2).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to @Component, ensure this @Inject annotation in mate.academy.lib is the single one used across the project and that no parallel, unused Inject annotation exists elsewhere, otherwise it can conflict with the requirement to use only @Component and @Inject (checklist item #2).

// Please test your Injector here. Feel free to push this class as a part of your solution
Injector injector = Injector.getInjector();
ProductService productService = null;
ProductService productService = (ProductService) injector.getInstance(ProductService.class);
List<Product> products = productService.getAllFromFile("products.txt");
products.forEach(System.out::println);
}
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/mate.academy/lib/Component.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package mate.academy.lib;

public @interface Component {
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
Comment on lines +8 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotations are correctly configured with @Retention(RetentionPolicy.RUNTIME) and @Target(ElementType.TYPE), which satisfies the requirement to make them usable via reflection at runtime.

Comment on lines +8 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotations are correctly configured with @Retention(RetentionPolicy.RUNTIME) and @Target(ElementType.FIELD), which satisfies the requirement to make them usable via reflection at runtime.

}
8 changes: 7 additions & 1 deletion src/main/java/mate.academy/lib/Inject.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package mate.academy.lib;

public @interface Inject {
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
Comment on lines +8 to +9

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotation is correctly marked with @Retention(RetentionPolicy.RUNTIME) and @Target(ElementType.TYPE), so it meets the requirements for runtime reflection and checklist item "Do not forget to add @Target to custom annotations." No changes needed here.

public @interface Inject {
}
11 changes: 11 additions & 0 deletions src/main/java/mate.academy/lib/InjectionException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package mate.academy.lib;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is missing its class definition here. This violates the requirement to have a fully implemented custom exception in the package actually used by the project, and also breaks checklist item #7 about throwing informative custom exceptions with causes attached.


public class InjectionException extends RuntimeException {
public InjectionException(String message) {
super(message);
}

public InjectionException(String message, Throwable cause) {
super(message, cause);
}
}
97 changes: 96 additions & 1 deletion src/main/java/mate.academy/lib/Injector.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,108 @@
package mate.academy.lib;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Injector {
private static final Injector injector = new Injector();
private final Map<Class<?>, Object> instances = new HashMap<>();
private final Map<Class<?>, Class<?>> interfaceImplementations = new HashMap<>();
Comment on lines +5 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The annotation usage here (@Component and @Inject) follows the requirement to use only these custom annotations and is correctly applied to a service implementation and its injectable fields.


private Injector() {
try {
List<Class<?>> classes = getClasses("mate.academy");
for (Class<?> clazz : classes) {
if (clazz.isAnnotationPresent(Component.class)) {
Class<?>[] interfaces = clazz.getInterfaces();
if (interfaces.length > 0) {
interfaceImplementations.put(interfaces[0], clazz);
}
}
}
Comment on lines +17 to +29

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Populating interfaceImplementations in the constructor and keeping it as a class field satisfies the requirement: "Make Interface Implementations map a class field. You can fill it in using Map.of()." Although you use new HashMap<>() instead of Map.of(), that’s acceptable as long as it stays a field and is initialized centrally.

} catch (Exception e) {
throw new RuntimeException("Can't initialize injector", e);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching a broad Exception and throwing a plain RuntimeException here does not use your custom InjectionException and slightly conflicts with checklist item #7, which asks for informative messages and propagating the caught exception in the custom exception type; consider replacing this with throw new InjectionException("Can't initialize injector", e);.

}
}

public static Injector getInjector() {
return injector;
}

public Object getInstance(Class<?> interfaceClazz) {
return null;
if (instances.containsKey(interfaceClazz)) {
return instances.get(interfaceClazz);
Comment on lines +16 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the instances map as a class field and checking it before creating new instances satisfies checklist item: "Let's check instances map before new instance creation." No change needed here.

}
Class<?> clazz = findImplementation(interfaceClazz);
if (clazz == null || !clazz.isAnnotationPresent(Component.class)) {
throw new InjectionException("Missing @Component annotation on class "
+ "or no implementation for " + interfaceClazz.getName());
}
try {
Constructor<?> constructor = clazz.getConstructor();
Object instance = constructor.newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Inject.class)) {
Object fieldInstance = getInstance(field.getType());
field.setAccessible(true);
field.set(instance, fieldInstance);
}
}
instances.put(interfaceClazz, instance);
return instance;
} catch (ReflectiveOperationException e) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Catching ReflectiveOperationException instead of multiple specific reflection exceptions matches the checklist advice: "It is better to replace many exceptions that have a common parent with a general parental exception."

throw new InjectionException("Can't create instance of " + clazz.getName(), e);
}
}

private Class<?> findImplementation(Class<?> interfaceClazz) {
if (interfaceClazz.isInterface()) {
return interfaceImplementations.get(interfaceClazz);
}
return interfaceClazz;
}

private static List<Class<?>> getClasses(String packageName)
throws ClassNotFoundException, IOException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
ArrayList<Class<?>> classes = new ArrayList<>();
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
return classes;
}

private static List<Class<?>> findClasses(File directory, String packageName)
throws ClassNotFoundException {
List<Class<?>> classes = new ArrayList<>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
for (File file : files) {
if (file.isDirectory()) {
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
String className = packageName + '.'
+ file.getName().substring(0, file.getName().length() - 6);
classes.add(Class.forName(className));
}
}
return classes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import mate.academy.lib.Component;
import mate.academy.service.FileReaderService;

@Component
public class FileReaderServiceImpl implements FileReaderService {
@Override
public List<String> readFile(String fileName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package mate.academy.service.impl;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is declared in the mate.academy.lib package but the class body is empty. This violates the description and checklist item #7, which require a custom exception implementation with informative messages and an overload that accepts a cause; you need to implement this class (or copy the working version from your other mate/academy/lib folder) in this package.


import java.math.BigDecimal;
import mate.academy.lib.Component;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class also imports mate.academy.lib.Component from the partially implemented mate.academy/lib folder. As with FileReaderServiceImpl, you need to ensure that the Component annotation implementation with proper @Retention(RUNTIME) and @Target is present in this exact package, not only in a different folder (checklist items #2 and #3).

import mate.academy.model.Product;
import mate.academy.service.ProductParser;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class relies on mate.academy.lib.Component, but in your project the src/main/java/mate.academy/lib package is not fully implemented while a different mate/academy/lib folder contains the actual annotation. Having duplicate/empty packages breaks the requirement for a single, consistent set of annotations (checklist item #2) and means this import may not resolve correctly; you should consolidate to one mate/academy/lib package with the implemented Component annotation.

@Component
public class ProductParserImpl implements ProductParser {
public static final int ID_POSITION = 0;
public static final int NAME_POSITION = 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

import java.util.List;
import java.util.stream.Collectors;
import mate.academy.lib.Component;
import mate.academy.lib.Inject;
import mate.academy.model.Product;
import mate.academy.service.FileReaderService;
Comment on lines 3 to 8

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is properly implemented with both a message-only and a message+cause constructor, which addresses checklist item #7 about throwing exceptions with informative messages and attaching the caught exception as the cause.

import mate.academy.service.ProductParser;
import mate.academy.service.ProductService;

@Component
Comment on lines +5 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProductServiceImpl correctly uses @Component on the implementation and @Inject on its dependencies. Just be aware that these annotations must come from the fully implemented mate.academy.lib package (which you have under mate/academy/lib), and you should remove the duplicate, partially implemented mate.academy/lib directory to avoid package conflicts (checklist item #2 about using only a single set of annotations).

public class ProductServiceImpl implements ProductService {
@Inject
private ProductParser productParser;
@Inject
private FileReaderService fileReaderService;

@Override
Expand Down
1 change: 1 addition & 0 deletions src/main/java/mate/academy/lib/Component.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is completely empty; per the task description, Component must be a custom annotation with @Retention(RUNTIME) and @Target, so this file is missing the required implementation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is completely empty; per the task description, Inject must be a custom annotation with @Retention(RUNTIME) and @Target, so this file is missing the required implementation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is completely empty; the assignment requires a custom InjectionException (extending RuntimeException and used by the Injector), so this file is missing the required implementation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is completely empty; the assignment’s core requirement is an Injector class that uses @Component and @Inject to create and wire instances, maintain an instances map and implementation map, and throw informative exceptions. This implementation is missing here.

1 change: 1 addition & 0 deletions src/main/java/mate/academy/lib/Inject.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty, but the project already has a fully implemented Component annotation in mate.academy.lib (with a dot in the path). Maintaining this empty duplicate violates checklist item #2: "You should use only @Component and @Inject annotations" and creates ambiguity about which annotation is actually used. Consider deleting this file or making sure only one consistent Component definition exists in the correct package.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is an empty duplicate of the Inject annotation. Per checklist item #2, you should have a single @Inject annotation used throughout the project; an empty, unused definition in another package can cause confusion and compilation issues. Remove this duplicate or ensure the implemented version is the only one present.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is declared as an empty file here, while there is a proper implementation elsewhere. This violates the spirit of checklist item #7 (informative custom exceptions) because this version provides no behavior and can shadow the real one. You should either remove this file or implement InjectionException here exactly as required (extending RuntimeException with constructors that accept message and cause).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Injector file in this package is empty, which means it does not implement any of the required DI behavior (component scanning, instance map, annotation checks, exception handling). The actual Injector exists in mate.academy.lib (dot path), so this duplicate is incorrect and can break the build. Remove this file or ensure only the fully implemented injector exists in the correct package so that all task requirements are met in a single, consistent Injector class.

1 change: 1 addition & 0 deletions src/main/java/mate/academy/lib/InjectionException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty, but the project imports mate.academy.lib.Component, and the task requires a @Component annotation with @Retention(RetentionPolicy.RUNTIME) and @Target(ElementType.TYPE). You need to implement this annotation here to satisfy the description and checklist items #2 and #3.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty, but the project imports mate.academy.lib.Inject, and the task requires an @Inject annotation with @Retention(RetentionPolicy.RUNTIME) and @Target(ElementType.FIELD). Implement this annotation here to meet the DI and checklist requirements.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is not implemented here, yet it is required as the custom runtime exception for injection errors with informative messages and chained causes (checklist item #7). You should add the class extending RuntimeException with appropriate constructors.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core Injector class is missing in this package, but Main and services expect mate.academy.lib.Injector. You must implement the injector here with the required behavior: using @Component and @Inject, checking @Component presence, maintaining instances and implementation maps, and handling reflection errors as described in the task and checklist.

1 change: 1 addition & 0 deletions src/main/java/mate/academy/lib/Injector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty but is supposed to define the @Component annotation for the DI framework. This violates checklist item #3: "Do not forget to add @Target to custom annotations" and the description requiring a @Component annotation with proper @Retention and @Target. You need to implement this annotation here or remove this duplicate package and rely on the properly implemented one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is empty but should define the @Inject annotation. As with Component, this violates checklist item #3 (missing @Target) and the requirement to configure annotations with @Retention so they can be used via reflection. Implement @Inject here (with @Target(ElementType.FIELD) and @Retention(RetentionPolicy.RUNTIME)) or remove this unused duplicate package.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InjectionException is not defined here, so any code importing mate.academy.lib.InjectionException from this package will fail. The task requires a custom exception with informative messages and the ability to wrap causes (checklist item #7). You should either implement InjectionException here with the required constructors or ensure you do not have this empty duplicate file/package.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Injector class is completely missing in this package. The core assignment requires an injector that uses @Component and @Inject, manages instances and interface-implementation mappings, checks annotations, and throws InjectionException with informative messages and consolidated reflection exceptions (checklist items #4, #5, #6, #7, #8). You need to either move your working Injector implementation into this package or remove this empty duplicate file/package. As-is, the project does not meet the core functional requirements in this package.

Loading