In my controller, I receive a string parameter on the basis of which I need to decide which service to call, how can I do the same in my Spring Boot application using Spring annotations?
For example: we have different types of cars. Now, on the basis of parameter in the request I should be able to decide which particular car service I should call.
How can I have a factory using annotations in Spring Boot, and objects should be returned from that factory on the basis of input.
I remember implementing support for this approach a few years ago, I believe inspired and using https://www.captechconsulting.com/blogs/combining-strategy-pattern-and-spring as the entry point to my utility library, use the following code snippets at your convenience:
Strategy.java
package ...
#Documented
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface Strategy {
Class<?> type();
String[] profiles() default {};
}
StrategyFactory.java
package ...
public class StrategyFactory {
private static final Logger LOG = Logger.getLogger( StrategyFactory.class );
private Map<Class<?>, Strategy> strategiesCache = new HashMap<Class<?>, Strategy>();
private String[] packages;
#PostConstruct
public void init() {
if (this.packages != null) {
Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
for (String pack : this.packages) {
Reflections reflections = new Reflections( pack );
annotatedClasses.addAll( reflections.getTypesAnnotatedWith( Strategy.class ) );
}
this.sanityCheck( annotatedClasses );
}
}
public <T> T getStrategy(Class<T> strategyClass) {
return this.getStrategy( strategyClass, null );
}
#SuppressWarnings("unchecked")
public <T> T getStrategy(Class<T> strategyClass, String currentProfile) {
Class<T> clazz = (Class<T>) this.findStrategyMatchingProfile( strategyClass, currentProfile );
if (clazz == null) {
throw new StrategyNotFoundException( String.format( "No strategies found of type '%s', are the strategies marked with #Strategy?", strategyClass.getName() ) );
}
try {
return (T) clazz.newInstance();
} catch (Exception e) {
throw ExceptionUtils.rethrowAs( e, StrategyException.class );
}
}
/**
* Checks to make sure there is only one strategy of each type(Interface) annotated for each profile Will throw an exception on startup if multiple strategies are mapped to the same profile.
* #param annotatedClasses a list of classes
*/
private void sanityCheck(Set<Class<?>> annotatedClasses) {
Set<String> usedStrategies = new HashSet<String>();
for (Class<?> annotatedClass : annotatedClasses) {
Strategy strategyAnnotation = AnnotationUtils.findAnnotation( annotatedClass, Strategy.class );
if (!strategyAnnotation.type().isAssignableFrom( annotatedClass )) {
throw new StrategyProfileViolationException( String.format( "'%s' should be assignable from '%s'", strategyAnnotation.type(), annotatedClass ) );
}
this.strategiesCache.put( annotatedClass, strategyAnnotation );
if (this.isDefault( strategyAnnotation )) {
this.ifNotExistAdd( strategyAnnotation.type(), "default", usedStrategies );
} else {
for (String profile : strategyAnnotation.profiles()) {
this.ifNotExistAdd( strategyAnnotation.type(), profile, usedStrategies );
}
}
}
}
private void ifNotExistAdd(Class<?> type, String profile, Set<String> usedStrategies) {
String key = this.createKey( type, profile );
if (usedStrategies.contains( key )) {
throw new StrategyProfileViolationException( String.format( "There can only be a single strategy for each type, found multiple for type '%s' and profile '%s'", type, profile ) );
}
usedStrategies.add( key );
}
private String createKey(Class<?> type, String profile) {
return String.format( "%s_%s", type, profile ).toLowerCase();
}
private boolean isDefault(Strategy strategyAnnotation) {
return (strategyAnnotation.profiles().length == 0);
}
private Class<?> findStrategyMatchingProfile(Class<?> strategyClass, String currentProfile) {
for (Map.Entry<Class<?>, Strategy> strategyCacheEntry : this.strategiesCache.entrySet()) {
Strategy strategyCacheEntryValue = strategyCacheEntry.getValue();
if (strategyCacheEntryValue.type().equals( strategyClass )) {
if (currentProfile != null) {
for (String profile : strategyCacheEntryValue.profiles()) {
if (currentProfile.equals( profile )) {
Class<?> result = strategyCacheEntry.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug( String.format( "Found strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, result ) );
}
return result;
}
}
} else if (this.isDefault( strategyCacheEntryValue )) {
Class<?> defaultClass = strategyCacheEntry.getKey();
if (LOG.isDebugEnabled()) {
LOG.debug( String.format( "Found default strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, defaultClass ) );
}
return defaultClass;
}
}
}
return null;
}
public void setPackages(String[] packages) {
this.packages = packages;
}
}
StrategyException.java
package ...
public class StrategyException extends RuntimeException {
...
}
StrategyNotFoundException.java
package ...
public class StrategyNotFoundException extends StrategyException {
...
}
StrategyProfileViolationException.java
package ...
public class StrategyProfileViolationException extends StrategyException {
...
}
Usage without Spring:
NavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
public interface NavigationStrategy {
public String naviateTo();
}
FreeNavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
#Strategy(type = NavigationStrategy.class)
public class FreeNavigationStrategy implements NavigationStrategy {
public String naviateTo() {
return "free";
}
}
LimitedPremiumNavigationStrategy.java
package com.asimio.core.test.strategy.strategies.navigation;
#Strategy(type = NavigationStrategy.class, profiles = { "limited", "premium" })
public class LimitedPremiumNavigationStrategy implements NavigationStrategy {
public String naviateTo() {
return "limited+premium";
}
}
Then
...
StrategyFactory factory = new StrategyFactory();
factory.setPackages( new String[] { "com.asimio.core.test.strategy.strategies.navigation" } );
this.factory.init();
NavigationStrategy ns = this.factory.getStrategy( NavigationStrategy.class );
String result = ns.naviateTo();
Assert.assertThat( "free", Matchers.is( result ) );
...
Or
...
String result = factory.getStrategy( NavigationStrategy.class, "limited" ).naviateTo();
Assert.assertThat( "limited+premium", Matchers.is( result ) );
...
Usage with Spring:
Spring context file:
<bean id="strategyFactory" class="com.asimio.core.strategy.StrategyFactory">
<property name="packages">
<list>
<value>com.asimio.jobs.feed.impl</value>
</list>
</property>
</bean>
IFeedProcessor.java
package ...
public interface IFeedProcessor {
void runBatch(String file);
}
CsvRentalsFeedProcessor.java
package ...
#Configurable(dependencyCheck = true)
#Strategy(type = IFeedProcessor.class, profiles = { "csv" })
public class CsvRentalsFeedProcessor implements IFeedProcessor, Serializable {
#Autowired
private CsvRentalsBatchReporter batchReporter;
...
}
Then
...
IFeedProcessor feedProcessor = this.strategyFactory.getStrategy( IFeedProcessor.class, feedFileExt );
feedProcessor.runBatch( unzippedFeedDir.getAbsolutePath() + File.separatorChar + feedFileName );
...
Notice CsvRentalsBatchReporter is "injected" in CsvRentalsFeedProcessor bean (a Strategy implementation) but StrategyFactory instantiates the strategy implementation using return (T) clazz.newInstance();, so what's needed to make this object Spring-aware?
First CsvRentalsFeedProcessor to be annotated with #Configurable(dependencyCheck = true) and when running the Java application this argument is needed in the java command: -javaagent:<path to spring-agent-${spring.version}.jar>
Related
I want to add a new group in ProductMetadataAutoConfiguration through a new class that extends ProductMetadataAutoConfiguration. I add the group in the CreateEntityView and it works fine. I do the same with UpdateEntityView but when I press "Save" to save the edited form the entity remains the same in the DB.
#EnableConfigurationProperties({CatalogMetadataProperties.class})
public class CustomProductMetadataConfig extends ProductMetadataAutoConfiguration {
public CustomProductMetadataConfig(CatalogMetadataProperties properties) {
super(properties);
}
#Bean
public ComponentSource characteristics() {
return registry -> {
for (DefaultProductType type : getAvailableProductTypes()) {
CreateEntityView<?> productCreate = (CreateEntityView<?>) registry
.get(String.format(ProductIds.CREATE, type.name()));
Group<?> prodGroup = productCreate.getGeneralForm()
.getGroup(ProductGroups.BASIC_INFORMATION)
.addGroup("Test Properties", getTestPropertiesGroup());
prodGroup.addField("custom", Fields.string()
.label("custom")
.order(1102));
prodGroup.addField("productSubtype", Fields.select()
.label("Product Sub Type")
.options(CharacteristicEnum.toOptions())
.order(1103));
UpdateEntityView<?> productUpdate = (UpdateEntityView<?>) registry
.get(String.format(ProductIds.UPDATE, type.name()));
Group<?> updateProdGroup = productUpdate.getGeneralForm()
.getGroup(ProductGroups.BASIC_INFORMATION);
updateProdGroup.addField("productSubtype", Fields.select()
.label("Product Sub Type")
.options(CharacteristicEnum.toOptions())
.order(1100));
updateProdGroup.addField("custom", Fields.string()
.label("custom")
.order(1101));
updateProdGroup.addGroup("Test Attributes", this.getTestPropertiesGroup())
.label("Additional Attributes")
.order(1101);
}
};
}
#NotNull
private Group<?> getTestPropertiesGroup() {
return Groups.basic()
.label("Test Properties")
.addGroup("testPropertiesSection", this.getDefaultInlineGroupTestProperties()
.order(3000))
.order((3003));
}
#NotNull
private Group<?> this.getDefaultInlineGroupTestProperties() {
return Groups.inline()
.label("testProperties")
.addField("testProperties.section1", Fields.string()
.label("section1")
.order(1000))
.addField("testProperties.section2", Fields.string()
.label("section2")
.order(1001))
.addField("testProperties.section3", Fields.string()
.label("section3")
.order(1002));
}
}
I am currently throwing a custom Exception - RequestValidationException.
ExceptionHandler class:
#RestControllerAdvice
#Slf4j
public class RestExceptionHandler {
#ExceptionHandler(value = RequestValidationException.class)
#ResponseStatus(HttpStatus.PRECONDITION_FAILED)
public Mono<HttpValidationError> handleRequestValidationException(RequestValidationException exception) {
log.error("Received exception: ", exception);
List<String> loc = new ArrayList<>();
loc.add(exception.getMessage());
ValidationError validationError = ValidationError.builder()
.loc(loc)
.msg(exception.getMessage())
.build();
List<ValidationError> errorMessages = new ArrayList<>();
errorMessages.add(validationError);
return Mono.just(HttpValidationError.builder().detail(errorMessages).build());
}
RequestValidationException class:
public class RequestValidationException extends RuntimeException {
public static final HttpStatus statusCode = HttpStatus.PRECONDITION_FAILED;
public RequestValidationException(String text) {
super(text);
}
public HttpStatus getStatusCode() {
return statusCode;
}
}
When the exception is thrown, I want the following response:
Code: 412
{
"detail": [
{
"loc": [
"No ID found to update. Please add an ID"
],
"msg": "No ID found to update. Please add an ID",
"type": null
}
]
}
What I am receiving is:
{
"error_code": 500,
"message": "No ID found to update. Please add an ID"
}
I checked the application logs and nowhere is the RestExceptionHandler being called. It just logs this error:
"level":"ERROR","logger":"c.a.c.c.c.AbstractController","thread":"boundedElastic-1","message":"Controller exception","stack":"<#384d845f> c.a.c.a.e.RequestValidationException
I just can't seem to figure out what's wrong with this code. Can someone point out what I might be missing? Thanks.
I was only able to get this to work with an implementation of AbstractErrorWebExceptionHandler as follows (sorry for the kotlin code):
#Component
#Order(-2)
class GlobalExceptionHandler(errorAttributes: ErrorAttributes,
resources: WebProperties.Resources,
applicationContext: ApplicationContext,
serverCodecConfigurer: ServerCodecConfigurer) : AbstractErrorWebExceptionHandler(errorAttributes, resources, applicationContext) {
companion object {
private val logger = KotlinLogging.logger {}
private const val HTTP_STATUS_KEY = "status"
private const val MESSAGE_KEY = "message"
private const val ERRORS_KEY = "errors"
}
init {
setMessageWriters(serverCodecConfigurer.writers)
}
override fun setMessageWriters(messageWriters: MutableList<HttpMessageWriter<*>>?) {
super.setMessageWriters(messageWriters)
}
override fun getRoutingFunction(errorAttributes: ErrorAttributes?): RouterFunction<ServerResponse> {
return RouterFunctions.route({ true }) { request ->
val error: Throwable = getError(request)
logger.error("Handling: ", error)
val errorProperties = getErrorAttributes(request, ErrorAttributeOptions.defaults())
when (error) {
is WebExchangeBindException -> {
....
}
else -> {
...
}
}
ServerResponse.status(HttpStatus.valueOf(errorProperties[HTTP_STATUS_KEY] as Int))
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(errorProperties)
}
}
}
In Java it would be something like:
#Component
#Order(-2)
public class GlobalExceptionHandler extends AbstractErrorWebExceptionHandler {
private static final String HTTP_STATUS_KEY = "status";
private static final String MESSAGE_KEY = "message";
private static final String ERRORS_KEY = "errors";
public GlobalExceptionHandler(ErrorAttributes errorAttributes, Resources resources, ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
super(errorAttributes, resources, applicationContext);
this.setMessageWriters(serverCodecConfigurer.getWriters());
}
public final void setMessageWriters(List messageWriters) {
super.setMessageWriters(messageWriters);
}
protected RouterFunction getRoutingFunction(ErrorAttributes errorAttributes) {
return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
Map<String, Object> errorPropertiesMap = getErrorAttributes(request,
ErrorAttributeOptions.defaults());
return ServerResponse.status(HttpStatus.BAD_REQUEST)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(errorPropertiesMap));
}
}
You can check more details at https://www.baeldung.com/spring-webflux-errors#global.
I made a very trivial mistake of extending the controller with AbstractController class which was causing this issue. Removing it solved my problem.
I'm trying to setup a prototype for using graphql across multiple java microservices, which requires me to join multiple graphql schema's into one.
I'm using 2 java-services and the ApolloServer with ApolloGateway; which shows the following schema in the playground:
type Client {
id: ID!
name: String
linkeduser: User
}
type Query {
user(id: ID!): User
users: [User]
client(id: ID!): Client
clients: [Client]
}
type User {
id: ID!
name: String
}
When running the simple query:
query client {
client(id: 1) {
id
name
linkeduser {
id
name
}
}
}
What I expect this to return is a client with a linkeduser; When debugging the client service gets queried, the user service gets queried, yet the response is:
{
"data": {
"client": {
"id": "1",
"name": "Bob",
"linkeduser": null
}
}
}
How do I get a linked user response in my client?
I've tried returning lists of users, a new client object with a list of linkedusers, a single user.
The example of https://github.com/apollographql/federation-jvm is the base of this code, though I've yet to see this working.
Code:
Service 1: Client
#WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet {
#Override
protected GraphQLConfiguration getConfiguration() {
return GraphQLConfiguration.with(getGraphQLSchema()).build();
}
private static GraphQLSchema getGraphQLSchema() {
InputStream inputStream = client.GraphQLService.class
.getClassLoader().getResourceAsStream("schema.graphqls");
TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getClient))
.build();
return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
.fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
.stream()
.map(values -> {
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return getSingleClient((String) id);
}
}
return null;
})
.collect(Collectors.toList()))
.resolveEntityType(env -> {
final Object src = env.getObject();
if (src instanceof Client) {
return env.getSchema().getObjectType("Client");
}
return null;
}).build();
}
private static Object getClient(DataFetchingEnvironment environment) {
switch (environment.getFieldDefinition().getName()) {
case "client":
return getSingleClient(environment.getArgument("id"));
case "clients":
return getAllClients();
default:
return null;
}
}
//... extra code with simple getters
}
With this schema :
extend type Query {
client(id: ID!): Client
clients: [Client]
}
type Client #key(fields: "id"){
id: ID!
name: String
}
Service 2: User
#WebServlet(loadOnStartup = 1, urlPatterns = "/graphql")
public class GraphQLService extends GraphQLHttpServlet {
#Override
protected GraphQLConfiguration getConfiguration() {
return GraphQLConfiguration.with(getGraphQLSchema()).build();
}
private static GraphQLSchema getGraphQLSchema() {
InputStream inputStream = user.GraphQLService.class
.getClassLoader().getResourceAsStream("schema.graphqls");
TypeDefinitionRegistry parse = new SchemaParser().parse(inputStream);
RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
.type("Query", builder -> builder.defaultDataFetcher(GraphQLService::getUser))
.build();
return com.apollographql.federation.graphqljava.Federation.transform(parse, runtimeWiring)
.fetchEntities(env -> env.<List<Map<String, Object>>>getArgument(_Entity.argumentName)
.stream()
.map(values -> {
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return getSingleUser((String) id);
}
}
return null;
})
.collect(Collectors.toList()))
.resolveEntityType(env -> {
final Object src = env.getObject();
if (src instanceof User) {
return env.getSchema().getObjectType("User");
}
return null;
})
.build();
}
private static Object getUser(DataFetchingEnvironment environment) {
switch (environment.getFieldDefinition().getName()) {
case "user":
return getSingleUser(environment.getArgument("id"));
case "users":
return getAllUsers();
default:
return null;
}
}
//... extra code with simple getters
}
With this schema :
type Query #extends{
user (id: ID!): User
users: [User]
}
type User #key(fields: "id") {
id: ID!
name: String
}
type Client #key(fields: "id") #extends{
id: ID! #external
linkeduser : User
}
Version in POM.xml
<graphql.version>14.0</graphql.version>
<graphql-tools.version>5.2.4</graphql-tools.version>
<graphql-servlet.version>9.0.1</graphql-servlet.version>
<graphql-federation-support.version>0.4.0</graphql-federation-support.version>
In user service, you need to return a pojo of the type client, with a getter for a linkeduser (only the extends fields need to be present):
if ("Client".equals(values.get("__typename"))) {
final Object id = values.get("id");
if (id instanceof String) {
return new Client((String) id, getSingleUser((String) id));
}
}
Also the resolveTypeEntity needs to resolve to said client
I have a JSP page wherein user has to enter some custom URL. I want to pass that custom url in #WebInitParam in my servlet
#WebServlet(name = "oauthCustomURL", initParams = {
#WebInitParam(name = "clientId", value = "123"),
#WebInitParam(name = "key", value = "***"),
#WebInitParam(name = "environment", value = "customUrl"),
}) //in value I want to pass the value entered by user
#WebInitParam's are used for the configuration of servlets implemented by third-party libraries. Usually, these libraries use methods getInitParameterNames() and getInitParameter() of abstract class GenericServlet (but you should check in library code for it).
For the dynamic setting of the #WebInitParam you can override those methods in your servlet implementation. Below is an example of how to do it.
#WebServlet(urlPatterns = "/abc/*")
public class DynamicInitParamServlet extends SomeCustomLibraryHttpServlet {
private static final String WEB_INIT_PARAM_NAME = "some.param.name";
private Integer webInitParamValue = null;
#Override
public void init(ServletConfig config) throws ServletException {
// calculate init param value dynamically,
// TODO: implement your own code
webInitParamValue = 2 * 3;
// call custom library servlet init after init parameter value is set
super.init(config);
}
#Override
public Enumeration<String> getInitParameterNames() {
if (webInitParamValue != null) {
final Set<String> initParameterNames = new HashSet<>(Collections.list(super.getInitParameterNames()));
initParameterNames.add(WEB_INIT_PARAM_NAME);
return Collections.enumeration(initParameterNames);
} else {
return super.getInitParameterNames();
}
}
#Override
public String getInitParameter(String name) {
if (WEB_INIT_PARAM_NAME.compareTo(name) == 0 && webInitParamValue != null) {
return "" + webInitParamValue;
} else {
return super.getInitParameter(name);
}
}
}
I am trying to use CDI, using #Inject for dependency injection but my object stays null and won't initialize... more precisely:
I have a webapplication with WeatherController which use a java application with all my modules. In the Java application I have a ForecastService where I try to initialize my repositories with CDI without success.
I tried/searched a lot. Hopefully somebody can help me here?
I have a web application which uses this controller:
#Path("/weather")
public class WeatherController {
private ForecastService forecastService;
//private ForecastRepository forecastRepository = new ForecastFakeDB();
//private ObservationRepository observationRepository = new ObservationFakeDB();
public WeatherController() {
//this.forecastService.setForecastRepository(forecastRepository);
//forecastService.setObservationRepository(observationRepository);
forecastService = new ForecastService();
}
//localhost:8080/DA_project_weatherPredictions/api/weather/observation/Leuven
#GET
#Produces({"application/json"})
#Path("/observation/{location}")
public Response getObservation(#PathParam("location") String location) {
try {
ObjectMapper mapper = new ObjectMapper();
Observation observation = forecastService.getCurrentObservation(location);
//Object to JSON in String
String jsonInString = mapper.writeValueAsString(observation);
return Response.status(200).entity(jsonInString).build();
} catch (Exception ex) {
System.out.println("error");
System.out.println(ex.getMessage());
ex.printStackTrace();
return null;
}
}
This works perfectly. This is my forecastService:
public class ForecastService implements Service {
#Inject
ForecastRepository forecastRepository;
#Inject
ObservationRepository observationRepository;
private Client client;
private WebTarget webTargetObservation, webTargetForecast;
public ForecastService() {
// WeatherRepositoryFactory weatherRepositoryFactory = new WeatherRepositoryFactory();
// forecastRepository = weatherRepositoryFactory.getForecastRepository(repository);
// observationRepository = weatherRepositoryFactory.getObservationRepository(repository);
loadWeather();
}
public void setForecastRepository(ForecastRepository forecastRepository) {
this.forecastRepository = forecastRepository;
}
public void setObservationRepository(ObservationRepository observationRepository) {
this.observationRepository = observationRepository;
}
public void loadWeather() {
//http://api.openweathermap.org/data/2.5/weather?units=metric&appid=12fa8f41738b72d954b6758d48e129aa&q=BE,Leuven
//http://api.openweathermap.org/data/2.5/forecast?units=metric&appid=12fa8f41738b72d954b6758d48e129aa&q=BE,Leuven
client = ClientBuilder.newClient();
webTargetObservation = client.target("http://api.openweathermap.org/data/2.5/weather")
.queryParam("mode", "json")
.queryParam("units", "metric")
.queryParam("appid", "12fa8f41738b72d954b6758d48e129aa");
webTargetForecast = client.target("http://api.openweathermap.org/data/2.5/forecast")
.queryParam("mode", "json")
.queryParam("units", "metric")
.queryParam("appid", "12fa8f41738b72d954b6758d48e129aa");
}
public Observation getCurrentObservation(String location) throws Exception {
Observation observation;
observation = observationRepository.getObservation(location);
if (observation == null) {
try {
//observation = webTargetObservation.queryParam("q", location).request(MediaType.APPLICATION_JSON).get(Observation.class);
Response response = webTargetObservation.queryParam("q", location).request(MediaType.APPLICATION_JSON).get();
String json = response.readEntity(String.class);
//System.out.println(json);
response.close();
observation = new ObjectMapper().readValue(json, Observation.class);
//System.out.println(observation.getWeather().getDescription());
}
catch (Exception e){
StringBuilder sb = new StringBuilder(e.toString());
for (StackTraceElement ste : e.getStackTrace()) {
sb.append("\n\tat ");
sb.append(ste);
}
String trace = sb.toString();
throw new Exception (trace);
//throw new Exception("Location not found");
}
this.observationRepository.addObservation(observation, location);
}
return observation;
}
So the problem is that my repositories stay null
#Alternative
public class ObservationDB implements ObservationRepository{
//as ID we can use the ASCI value of the String key .. example uklondon to ASCII
public ObservationDB(String name) {
}
#Override
public Observation getObservation(String location) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void addObservation(Observation observation, String location) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Mermory DB:
#Default
public class ObservationFakeDB implements ObservationRepository {
//example String key : beleuven, uklondon
private static Map<String, Observation> observations;
public ObservationFakeDB() {
observations = new HashMap<>();
}
#Override
public Observation getObservation(String location) {
return observations.get(location);
}
#Override
public void addObservation(Observation observation, String location) {
observations.put(location, observation);
}
}
I have a beans.xml, I thought beans.xml, #Inject, #Default en #Alternative would make this work. I tried #Dependent, #Applicationscoped.
EDIT:
I also often get this warning on Netbeans.
My beans.xml
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
You need to let your CDI container manages the lifecycle of all your beans to allow it to resolve and inject properly their dependencies.
So, in your case you should not create yourself the instance of ForecastService, you should rather delegate it to the CDI container by simply annotating the field forecastService with #Inject this way its dependencies will be automatically resolved and set by the container.
public class WeatherController {
#Inject
private ForecastService forecastService;
...