ListenerObject not found in imports for Ehcache 3? - ehcache

I am trying to implement a listener for an Ehcache 3.3.1 project using the code below. Can anyone suggest a solution for the ListenerObject? I can't seem to find it anywhere,except on the docs page I got the code from
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheEventListenerConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.event.EventType;
public class CacheHandler{
private Logger LOG = Logger.getLogger(this.getClass().getName());
private String cacheName="basicCache";
public Cache cache;
public CacheHandler(){
if(cache==null)
cache=initCache();
}
private Cache initCache(){
CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(new ListenerObject(), EventType.CREATED, EventType.UPDATED)
.unordered().asynchronous();
final CacheManager manager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache(cacheName,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10))
.add(cacheEventListenerConfiguration)
).build(true);
final Cache<String, String> cache = manager.getCache("foo", String.class, String.class);
return cache;
}
public Cache getCache(){
if(cache==null)
cache=initCache();
return cache;
}
}

It is indeed not mentioned but since it is only one method it is normally easy to figure out.
Here is an example:
public class ListenerObject implements CacheEventListener<Object, Object> {
#Override
public void onEvent(CacheEvent<? extends Object, ? extends Object> event) {
System.out.println(event);
}
}
The real one used in the documentation is here.
Then, I've played a bit with your code to real production usable code.
public class CacheHandler implements AutoCloseable {
private static final String CACHE_NAME = "basicCache";
private final Cache<String, String> cache;
private final CacheManager cacheManager;
public CacheHandler() {
cacheManager = initCacheManager();
cache = cacheManager.getCache(CACHE_NAME, String.class, String.class);
}
private CacheManager initCacheManager(){
CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(new ListenerObject(), EventType.CREATED, EventType.UPDATED)
.ordered().synchronous();
return CacheManagerBuilder.newCacheManagerBuilder()
.withCache(CACHE_NAME,
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(10))
.add(cacheEventListenerConfiguration)
).build(true);
}
public Cache getCache(){
return cache;
}
#Override
public void close() {
cacheManager.close();
}
public static void main(String[] args) {
try(CacheHandler handler = new CacheHandler()) {
Cache<String, String> cache = handler.getCache();
cache.put("a", "b");
cache.putIfAbsent("a", "c");
}
}
}
Some comments:
I assumed you want singleton cache kept in a variable. So that's what I did. The lazy initCache wasn't useful because the withCache tells Ehcache to create the cache when creating the cache manager.
We will want to keep a reference to the CacheManager in order to close it at the end.
The getCache was retrieving "foo", not "basicCache"

Related

How do I configure consistency level with Spring Boot and AWS Keyspaces?

I hope you can help me, I am doing a crud with spring boot and keyspaces (cassandra aws), spring has the default level of Consistency level in ONE and I have not been able to write the data since I get the following error:
"message": "Query; CQL [INSERT INTO tabledemoach (address,ciiu,creation_date,email,id,name,phone,state,user_activation_status) VALUES (?,?,?,?,?,?,?,?,?)]; Consistency level LOCAL_ONE is not supported for this operation. Supported consistency levels are: LOCAL_QUORUM; nested exception is com.datastax.oss.driver.api.core.servererrors.InvalidQueryException: Consistency level LOCAL_ONE is not supported for this operation. Supported consistency levels are: LOCAL_QUORUM",
I don't know how to configure the consistency level, I have tried several solutions on the internet and none have worked for me.
i have the following code
#Configuration
#EnableCassandraRepositories(basePackages = "com.demo")
public class AppConfig {
private final static String KEYSPACE = "demo";
#Primary
public #Bean CqlSession session() {
return CqlSession.builder().withKeyspace(KEYSPACE).build();
}
}
#Table(value = "tabledemoach")
#Data
public class User {
#PrimaryKey
private int id;
private String phone;
private String name;
private String address;
private String email;
private int ciiu;
private String state;
private String user_activation_status;
private LocalDate creation_date;
}
#Override
public void createUser(User user) {
List<User> userFind = (List<User>) userRepository.findAll();
var userList =userFind.stream().map(x -> x.getPhone());
var repeated = (userList.filter(x ->
x.contains(user.getPhone()))).collect(Collectors.toList());
if(repeated.size() <= 0){
userRepository.save(user);
}
}
There are several ways to configure the consistency level. You can define the default consistency in an application.conf file with:
datastax-java-driver {
basic {
request {
consistency = LOCAL_QUORUM
}
}
}
You can also configure it when using InsertOptions and UpdateOptions. For example:
InsertOptions insertOptions =
org.springframework.data.cassandra.core.InsertOptions.builder()
.consistencyLevel(ConsistencyLevel.LOCAL_QUORUM)
.build();
You can also use the #Consistency annotation, for example:
#Consistency(ConsistencyLevel.LOCAL_QUORUM)
List<Person> findByLastname(String lastname);
Finally with QueryOptions:
QueryOptions queryOptions =
newQueryOptions.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
Here is an example of Spring Boot and Amazon Keyspaces
https://github.com/aws-samples/amazon-keyspaces-examples/tree/main/java/datastax-v4/spring
package com.example.demo;
import com.datastax.oss.driver.api.core.CqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.cassandra.core.CassandraOperations;
import org.springframework.data.cassandra.core.cql.CqlTemplate;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
#SpringBootApplication
#EnableCassandraRepositories(basePackages="com.example.model")
public class DemoApplication implements CommandLineRunner {
#Autowired
private CassandraOperations cassandraTemplate;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
CqlTemplate cqlTemplate = (CqlTemplate) cassandraTemplate.getCqlOperations();
CqlSession session = cqlTemplate.getSession();
int count = session.execute ("SELECT * FROM system.peers").all().size();
System.out.println("Number of hosts: "+ count);
}
}

Is there a way to override application properties programmatically?

As mentioned in the Quarkus documentation, config values can be read using
String databaseName = ConfigProvider.getConfig().getValue("database.name", String.class);
Optional<String> maybeDatabaseName = ConfigProvider.getConfig().getOptionalValue("database.name", String.class);
Is there any possibility to set an application property during runtime?
I want to set quarkus.hibernate-orm.database.default-schema during the startup of the application. This should happen programmatically (in Java code), so without the definition of the property from outside.
Yes, it is possible.
You can for example add:
package org.acme.config;
import org.eclipse.microprofile.config.spi.ConfigSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class InMemoryConfigSource implements ConfigSource {
private static final Map<String, String> configuration = new HashMap<>();
static {
configuration.put("my.prop", "1234");
}
#Override
public int getOrdinal() {
return 275;
}
#Override
public Set<String> getPropertyNames() {
return configuration.keySet();
}
#Override
public String getValue(final String propertyName) {
return configuration.get(propertyName);
}
#Override
public String getName() {
return InMemoryConfigSource.class.getSimpleName();
}
}
in your code and make it known to Quarkus using Java's Service Loader mechanism, by adding the src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource file containing:
org.acme.config.InMemoryConfigSource.
See this guide for more details.

Sprint boot - Auto configure to call a REST service on startup

I have a requirement to create an auto-configuration for service call on spring-boot startup.
i.e., During spring-boot startup, the below service has to be called.
#PostMapping(path = "/addProduct", produces = "application/json", consumes = "application/json")
public #ResponseBody String addProduct(#RequestBody String productStr) {
..<My code>..
}
The add product requires an input like:
{
"product":"test",
"price":"10"
}
This will internally call a database service.
During startup, the json input provided in the console should be fed to this service.
I have no idea on how to achieve this. Verified a couple of Spring documentation. But those does'nt suit the requirement.
Kindly help in explaining a way or providing a right documentation to achieve this.
One way to do this is by implementing ApplicationRunner like this :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
#Component
public class ApplicationInitializer implements ApplicationRunner {
private ProductController productController;
public ApplicationInitializer(ProductController productController) {
this.productController = productController;
}
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
String response = productController.add(product);
System.out.println(response);
}
}
The run method will be invoked at startup with arguments passed in the command line like this : java -jar yourApp.jar --product="{\"name\":\"test\", \"price\":\"15\"}".
And you need a class to map the json to an object like this :
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
You can also call your Controller using the RestTemplate (or WebClient) if needed :
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
#Component
public class ApplicationInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
String productArg = args.getOptionValues("product").get(0); // Assume that you will have only one product argument
ObjectMapper mapper = new ObjectMapper();
Product product = mapper.readValue(productArg, Product.class);
RestTemplate restTemplate = new RestTemplate();
String response = restTemplate.postForObject("http://localhost:8080/products", product, String.class);
System.out.println(response);
}
}
Such requirement can be achieved by using an init() method annotated with #PostConstruct in a bean.
e.g.
#Component
public class Foo {
#PostConstruct
public void init() {
//Call your service
}
}

Spring Boot Custom Cache Resolver and Cache Manager using HazelCast

I have multiple questions related to HazelCast core as well as Spring Boot cache API.
Let me lay out the scenario first.
We have a monitoring system for monitoring multiple network infrastructures.
I have a Spring Boot app which could be deployed as multiple nodes behind a load-balancer.
In addition to that, this same app can work for multiple infrastructures by just running it with different profile such as infra-1-prod, infra-2-prod etc.
Its horizontally scalable as well as versatile. This nature is achieved by running the application with different profiles.
Along with other things, this profile change, changes the underlying DB connections to a relational database which contains the configuration data for a particular infrastructure.
Have a look at the relevant architecture for the application
The same spring boot application could be run as a node for different infrastructures spawning its own HazelCast instance node. If we have 6 nodes for the application, there will be 6 nodes for the HazelCast cluster. All of them will be in sync.
Now I have a Repository named RuleRepository which returns the Rule data for a particular Rule Alias.
#Repository
public interface RuleRepository extends JpaRepository<Rule, Long> {
#Cacheable(value = Constants.CACHE_ALIAS)
Optional<Rule> findByAlias(String ruleAlias);
//some other functions
}
Now the problem is, as the Rule Aliases are auto generated by DB sequences, an alias R_123 points to different data for Infra-1 and Infra-2 nodes but because all the HazelCast nodes are in sync, incorrect data is overridden.
For this, I thought of giving different names to the cache for every infrastructure so that the cached data don't get jumbled.
Doing this is not straight forward because we can't inject properties into the cache names. For this we need to implement our own custom CacheResolver and CacheManager.
I will lay out my understanding of HazelCast before I ask the first question.
Every HazelCast Instance can have multiple Map Configurations which are basically just different caches. Every CacheManager can be linked with a Single HazelCast instance which will internally contain multiple caches.
Question 1: If the relationship between CacheManager and HazelCastInstance is one-to-one then how will I determine which method data will be cached into which cache (Map Config).
Here is the incomplete implementation I have with me currently
public class CacheableOperations {
private final CacheManager cacheManager;
private final CacheManager noOpCacheManager;
public CacheableOperations(CacheManager cacheManager, CacheManager noOpCacheManager) {
this.cacheManager = cacheManager;
this.noOpCacheManager = noOpCacheManager;
}
private Map<String, CacheableOperation<?>> opMap;
public void init() {
List<CacheableOperation<? extends Class>> ops = new ArrayList<>();
ops.add(new CacheableOperation.Builder(RuleRepository.class)
.method("findByAlias")
.cacheManager(cacheManager)
.build());
postProcessOperations(ops);
}
public CacheableOperation<?> get(CacheOperationInvocationContext<?> context) {
final String queryKey = getOperationKey(context.getTarget().getClass().getName(),
context.getMethod().getName());
return opMap.get(queryKey);
}
private void postProcessOperations(List<CacheableOperation<? extends Class>> ops) {
Map<String, CacheableOperation<?>> tempMap = new HashMap<>();
for (CacheableOperation<?> op : ops) {
for (String methodName : op.getMethodNames()) {
tempMap.put(getOperationKey(op.getTargetClass().getName(), methodName), op);
}
}
opMap = ImmutableMap.copyOf(tempMap);
}
private String getOperationKey(String first, String second) {
return String.format("%s-%s", first, second);
}
Here is the class for CacheConfiguration
#Configuration
#AllArgsConstructor
public class CacheConfiguration extends CachingConfigurerSupport {
private final CacheProperties cacheProperties;
private SysdiagProperties sysdiagProperties;
#Bean
#Override
public CacheManager cacheManager() {
return new HazelcastCacheManager(hazelcastInstance());
}
#Bean
#Profile("client")
HazelcastInstance hazelcastInstance() {
Config config = new Config();
config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(sysdiagProperties.getCache().getMemberIps()).setEnabled(true);
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
config.setInstanceName("restapi-master-cache-" + sysdiagProperties.getServiceName());
return Hazelcast.newHazelcastInstance(config);
}
#Bean
#Override
public CacheResolver cacheResolver() {
return new CustomCacheResolver(cacheProperties, operations(), noOpCacheManager());
}
#Bean
public CacheManager noOpCacheManager() {
return new NoOpCacheManager();
}
#Bean
public CacheableOperations operations() {
CacheableOperations operations = new CacheableOperations(cacheManager(), noOpCacheManager());
operations.init();
return operations;
}
And here is the CacheableOperation class
public class CacheableOperation<T> {
private final Class<T> targetClass;
private final String[] methodNames;
private final CacheManager cacheManager;
private CacheableOperation(Class<T> targetClass, String[] methodNames, CacheManager cacheManager) {
this.targetClass = targetClass;
this.methodNames = methodNames;
this.cacheManager = cacheManager;
}
public Class<T> getTargetClass() {
return targetClass;
}
public String[] getMethodNames() {
return methodNames;
}
public CacheManager getCacheManager() {
return cacheManager;
}
public static class Builder<T> {
private final Class<T> targetClass;
private String[] methodNames;
private CacheManager cacheManager;
private Map<String, Method> methods = new HashMap<>();
public Builder(Class<T> targetClass) {
this.targetClass = targetClass;
Arrays.stream(targetClass.getDeclaredMethods())
.forEachOrdered(method -> methods.put(method.getName(), method));
}
public Builder<T> method(String... methodNames) {
this.methodNames = methodNames;
return this;
}
public Builder<T> cacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
return this;
}
public CacheableOperation<T> build() {
checkArgument(targetClass != null);
checkArgument(ArrayUtils.isNotEmpty(methodNames));
checkArgument(Arrays.stream(methodNames).allMatch(name -> methods.get(name) != null));
return new CacheableOperation<T>(targetClass, methodNames, cacheManager);
}
}
}
And finally the CacheResolver
public class CustomCacheResolver implements CacheResolver {
private final CacheableOperations operations;
private final CacheProperties cacheProperties;
private final CacheManager noOpCacheManager;
public CustomCacheResolver(CacheProperties cacheProperties, CacheableOperations operations, CacheManager noOpCacheManager) {
this.cacheProperties = cacheProperties;
this.operations = operations;
this.noOpCacheManager = noOpCacheManager;
}
#Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
if (!cacheProperties.isEnabled()) {
return getCaches(noOpCacheManager, context);
}
Collection<Cache> caches = new ArrayList<>();
CacheableOperation operation = operations.get(context);
if (operation != null) {
CacheManager cacheManager = operation.getCacheManager();
if (cacheManager != null) {
caches = getCaches(cacheManager, context);
}
}
return caches;
}
private Collection<Cache> getCaches(CacheManager cacheManager, CacheOperationInvocationContext<?> context) {
return context.getOperation().getCacheNames().stream()
.map(cacheName -> cacheManager.getCache(cacheName))
.filter(cache -> cache != null)
.collect(Collectors.toList());
}
}
Question 2: In this whole code base, I cannot find the linkage between a Cache Name and a Method Name which I did in the first snippet. All I could see is a link between the method name and the cacheManager instance. Where do I define that?
All the questions and documentation I read about Spring Boot and HazelCast, does not seem to go in great depth in this case.
Question 3: Can someone define the role of a CacheResolver and a CacheManager in a straight forward manner for me.
Thanks for the patience. Answer to even one of the question might help me a lot. :)
You can specify the parameter in the #Cacheable annotation. For example:
#Cacheable("books")
public String getBookNameByIsbn(String isbn) {
return findBookInSlowSource(isbn);
}
That will decide on the name internal map/cache used.

How to fix xml-less autowiring of service

When I call a service directly in my main() I can query the database and things work fine. When a jersey request comes in and maps the JSON to NewJobRequest I can't use my service because the #Autowire failed.
My app:
public class Main {
public static final URI BASE_URI = getBaseURI();
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost/").port(9998).build();
}
protected static HttpServer startServer() throws IOException {
ResourceConfig rc = new PackagesResourceConfig("com.production.api.resources");
rc.getFeatures()
.put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
return GrizzlyServerFactory.createHttpServer(BASE_URI, rc);
}
public static void main(String[] args) throws IOException {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
//if this is uncommented, it'll successfully query the database
//VendorService vendorService = (VendorService)ctx.getBean("vendorService");
//Vendor vendor = vendorService.findByUUID("asdf");
HttpServer httpServer = startServer();
System.out.println(String.format("Jersey app started with WADL available at " + "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...", BASE_URI, BASE_URI));
System.in.read();
httpServer.stop();
}
}
My Resource (controller):
#Component
#Path("/job")
public class JobResource extends GenericResource {
#Path("/new")
#POST
public String New(NewJobRequest request) {
return "done";
}
}
Jersey is mapping the JSON post to:
#Component
public class NewJobRequest {
#Autowired
private VendorService vendorService;
#JsonCreator
public NewJobRequest(Map<String, Object> request) {
//uh oh, can't do anything here because #Autowired failed and vendorService is null
}
}
VendorService:
#Service
public class VendorService extends GenericService<VendorDao> {
public Vendor findByUUID(String uuid) {
Vendor entity = null;
try {
return (Vendor)em.createNamedQuery("Vendor.findByUUID")
.setParameter("UUID", uuid)
.getSingleResult();
} catch (Exception ex) {
return null;
}
}
}
-
#Service
public class GenericService<T extends GenericDao> {
private static Logger logger = Logger.getLogger(Logger.class.getName());
#PersistenceContext(unitName = "unit")
public EntityManager em;
protected T dao;
#Transactional
public void save(T entity) {
dao.save(entity);
}
}
My service config:
#Configuration
public class Config {
#Bean
public VendorService vendorService() {
return new VendorService();
}
}
My config
#Configuration
#ComponentScan(basePackages = {
"com.production.api",
"com.production.api.dao",
"com.production.api.models",
"com.production.api.requests",
"com.production.api.requests.job",
"com.production.api.resources",
"com.production.api.services"
})
#Import({
com.production.api.services.Config.class,
com.production.api.dao.Config.class,
com.production.api.requests.Config.class
})
#PropertySource(value= "classpath:/META-INF/application.properties")
#EnableTransactionManagement
public class Config {
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USER = "db.user";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
#Resource
Environment environment;
#Bean
public DataSource dataSource() {
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
dataSource.setUser(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USER));
dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return dataSource;
}
#Bean
public JpaTransactionManager transactionManager() throws ClassNotFoundException {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() throws ClassNotFoundException {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceUnitName("unit");
entityManagerFactoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistence.class);
Properties jpaProperties = new Properties();
jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
}
The #Path and #POST annotations are JAX-RS, not Spring. So the container is instantiating your endpoints on its own, without any knowledge of Spring beans. You are most likely not getting any Spring logging because Spring is not being used at all.
I've figured out the issue and blogged about it here: http://blog.benkuhl.com/2013/02/how-to-access-a-service-layer-on-a-jersey-json-object/
In the mean time, I'm also going to post the solution here:
I need to tap into the bean that Spring already created so I used Spring's ApplicationContextAware
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext (ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
And then used that static context reference within my object to be mapped to so I can perform lookups in the service:
public class NewJobRequest {
private VendorService vendorService;
public NewJobRequest() {
vendorService = (VendorService) ApplicationContextProvider.getApplicationContext().getBean("vendorService");
}
#JsonCreator
public NewJobRequest(Map<String, Object> request) {
setVendor(vendorService.findById(request.get("vendorId")); //vendorService is null
}
....
}

Resources