1

I have a interface like this :

@Singleton
public interface StorageEngine {
    String upload(InputStream inputStream);

    InputStream download(String fileName);

    int size(String fileName);

    boolean delete(String fileName);
}

and multiple implementation :

public class LocalFileSystemEngine implements StorageEngine {
   // Implement methods ...
}

public class DropboxEngine implements StorageEngine {
   // Implement methods ...
}

public class GoogleDriveEngine implements StorageEngine {
   // Implement methods ...
}

and this Producer class :

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ApplicationScoped
public class StorageProducer {
    private static final Logger logger = LoggerFactory.getLogger(StorageProducer.class);

    @Inject
    @ConfigProperty(name = "storage.engine-type",defaultValue = "local-filesystem")
    private String engineType;

    @Inject
    @Any
    private Instance<StorageEngine> storageEngineInstance;

    @Produces
    public StorageEngine getStorageEngine() {
        // How to implement ???
    }
}

my goal is :
read engine type from microprofile properties file and produce storage engine .
Is there any way to inject StorageEngine without qualifier annotation ?

I need to have only this on injection point class:

@MultipartConfig
@WebServlet(urlPatterns = "/file")
public class FileServlet extends HttpServlet {
     @Inject
    private StorageEngine storageEngine;    
    

    // Implement service method 
} 

anyone can help me ?

3 Answers 3

2

All of the solutions are too complicated. Qualifiers are not needed.

Restrict the bean types of the concrete implementations and then have your producer method pick the right one:

@Typed(LocalFileSystemEngine.class) // <-- NOTE
public class LocalFileSystemEngine implements StorageEngine {
   // Implement methods ...
}

@Typed(DropboxEngine.class) // <-- NOTE
public class DropboxEngine implements StorageEngine {
   // Implement methods ...
}

@Typed(GoogleDriveEngine.class) // <-- NOTE
public class GoogleDriveEngine implements StorageEngine {
   // Implement methods ...
}

@Produces
public StorageEngine getStorageEngine(@ConfigProperty(name = "storage.engine-type",
                                                      defaultValue = "local-filesystem")
                                      String type,
                                      LocalFilesystemEngine lfe,
                                      DropboxEngine de,
                                      GoogleDriveEngine gde) {
  return switch (type) {
    case "local-filesystem" -> lfe;
    case "google-drive" -> gde;
    case "dropbox" -> de;
    default -> throw new CreationException();
  };
}
Sign up to request clarification or add additional context in comments.

1 Comment

Wowww , Great answer , Thank you .
0

The idiomatic way to do it would be to use qualifiers, but to use them inside getStorageEngine().

@Produces
public StorageEngine getStorageEngine() {

    if (engineType.equals("type one")) {
        //TypeOne.class is a qualifier
        storageEngineInstance.select(TypeOne.class);
    } else if //repeat for all engine types
}

If you really do not want to use qualifiers, Instance is also iterable

@Produces
public StorageEngine getStorageEngine() {

    if (StorageEngine storageEngine : storageEngineInstance) {
       if (storageEngine.someValue().equals(engineType.someValue())) {
           return storageEngine;
       }
    } 
}

1 Comment

I do this , but does not work on liberty application server : WELD-001409: Ambiguous dependencies for type StorageEngine with qualifiers @Default
0

I finally found a good solution .
Actually we still need @Qualifier annotation .

first create a annotation like this :

@Qualifier
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface StorageType {
    String value();
}

now create annotation literal for that :

public class StorageTypeLiteral extends AnnotationLiteral<StorageType> implements StorageType {

    private final String value;

    public StorageTypeLiteral(String value) {
        this.value = value;
    }

    @Override
    public String value() {
        return value;
    }
}

Change implementations :

@StorageType("local-filesystem")
public class LocalFileSystemEngine implements StorageEngine {
   // Implement methods ...
}

@StorageType("Dropbox")
public class DropboxEngine implements StorageEngine {
   // Implement methods ...
}

@StorageType("GoogleDrive")
public class GoogleDriveEngine implements StorageEngine {
   // Implement methods ...
}

now update producer class like this :

@ApplicationScoped
public class StorageProducer {
    private static final Logger logger = LoggerFactory.getLogger(StorageProducer.class);

    @Inject
    @ConfigProperty(name = "storage.engine-type",defaultValue = "local-filesystem")
    private String engineType;

    @Inject
    @Any
    private Instance<StorageEngine> storageEngineInstance;

    @Produces
    public StorageEngine getStorageEngine() {
        Instance<StorageEngine> instance = storageEngineInstance.select(new StorageTypeLiteral(engineType));
        if (!instance.isResolvable())
            throw new IllegalArgumentException("Storage %s not implemented yet".formatted(engineType));
        return instance.get();
    }
}

It's work .
Thank you .

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.