CacheBuilder.newBuilder().build is ambiguous - java

I'm trying to use a snippet of code for a Stash plugin, but the compiler keeps giving me an error that I can't seem to solve. It's using com.google.common.cache.Cache (Guava)
static final RepositorySettings DEFAULT_SETTINGS = new RepositorySettings(0);
private final PluginSettings pluginSettings;
private final Cache<Integer, RepositorySettings> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Integer, RepositorySettings>()
{
#Override
public RepositorySettings load(#Nonnull Integer repositoryId)
{
#SuppressWarnings("unchecked")
Map<String, String> data = (Map) pluginSettings.get(repositoryId.toString());
return data == null ? DEFAULT_SETTINGS : deserialize(data);
}
});
The .build is giving me the following error
The method build(CacheLoader<? super Integer,RepositorySettings>) is ambiguous for the type CacheBuilder<Object,Object>

Cache has a build() method that takes no parameters, LoadingCache on the other hand has a build() method that takes CacheLoader as a parameter.
private final LoadingCache<Integer, RepositorySettings> cache = CacheBuilder.newBuilder().build(
new CacheLoader<Integer, RepositorySettings>() {
#Override
public RepositorySettings load(#Nonnull Integer repositoryId) {
#SuppressWarnings("unchecked")
Map<String, String> data = (Map) pluginSettings.get(repositoryId.toString());
return data == null ? DEFAULT_SETTINGS : deserialize(data);
}
});
This should work.
As reference:
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/CacheBuilder.html

Related

Unable to select value for the custom field that has multiple options in JIRA Java client library

I have a custom field that has multiple options.
The applicable options for this field are:
Other / Non-Category Specific
Installation & Services
Pro
Other
So, I would need to choose the option "Other / Non-Category Specific" while creating an issue using Java JIRA client API. And, JIRA runs on our data center, not in the cloud.
Dependency used:
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-rest-java-client-app</artifactId>
<version>5.2.0</version>
</dependency>
Implementing the AuthenticationHandler for Personal access tokens:
public class BearerHttpAuthenticationHandler implements AuthenticationHandler {
private static final String AUTHORIZATION_HEADER = "Authorization";
private final String token;
public BearerHttpAuthenticationHandler(final String token) {
this.token = token;
}
#Override
public void configure(Builder builder) {
builder.setHeader(AUTHORIZATION_HEADER, "Bearer " + token);
}
}
Creating the bean of JiraRestClient
#Bean
public JiraRestClient asynchronousJiraRestCleint() {
JiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
//Personal access token
BearerHttpAuthenticationHandler handler = new BearerHttpAuthenticationHandler("Personal access token is used");
JiraRestClient restClient = factory.create(URI.create("https://host-name/jira"), handler);
return restClient;
}
I have overridden the setAssigneeName method, to handle 'null'. This means no need to assign a name for the assignee by default.
public class IssueInputBuilderCustom extends com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder {
#Override
public IssueInputBuilder setAssigneeName(String assignee) {
return setFieldInput(
new FieldInput(IssueFieldId.ASSIGNEE_FIELD, ComplexIssueInputFieldValueCustom.with("name", assignee)));
}
}
public class ComplexIssueInputFieldValueCustom extends com.atlassian.jira.rest.client.api.domain.input.ComplexIssueInputFieldValue{
public ComplexIssueInputFieldValueCustom(Map<String, Object> valuesMap) {
super(valuesMap);
}
public static ComplexIssueInputFieldValue with(String key, Object value) {
Map<String, Object> complexMap = new HashMap<>();
complexMap.put(key, value);
return new ComplexIssueInputFieldValue(complexMap);
}
}
Service:
#Autowired
private JiraRestClient restClient;
public void createIssue(){
IssueRestClient issueClient = restClient.getIssueClient();
MetadataRestClient metadataClient = restClient.getMetadataClient();
Map<String, Long> priorityMap = new HashMap<>();
Map<String, Long> issueMap = new HashMap<>();
Map<String, String> fieldsMap = new HashMap<>();
// To get the list of all types of priorities
metadataClient.getPriorities().claim().forEach(e -> priorityMap.put(e.getName(), e.getId()));
// To get the list of all issue types
metadataClient.getIssueTypes().claim().forEach(s -> issueMap.put(s.getName(), s.getId()));
// To get the list of all fields, which gives you the custom fields as well
metadataClient.getFields().claim().forEach(a -> fieldsMap.put(a.getName(), a.getId()));
List<Object> list = new ArrayList<>();
list.add("Pro");
IssueInputBuilderCustom issueInputBuilder = new IssueInputBuilderCustom();
issueInputBuilder.setProjectKey("project key")
.setAssigneeName(null)
.setIssueTypeId(issueMap.get("Type of Request"))
.setPriorityId(priorityMap.get("High"))
.setSummary("summary of the ticket")
.setDueDate(DateTime.now())
.setFieldValue("customfield_10050", list); **// Assigning the value for the custom field here**
IssueInput issueInput = issueInputBuilder.build();
BasicIssue issue = issueClient.createIssue(issueInput).claim();
}
After constructing the issueInputBuilder using build() method for the below attempts and getting, these errors after invoking createIssue(issueInput).claim() method:
Attempt 1
List<Object> list = new ArrayList<>();
list.add("Pro");
.setFieldValue("customfield_10050", list); // Assigning the value for the custom field here
issueInput:
customfield_10806=FieldInput{id=customfield_10806, value=[Pro]}
Error:
RestClientException{statusCode=Optional.of(400), errorCollections=[ErrorCollection{status=400, errors={Change Type=expected Object}, errorMessages=[]}]}
Attempt 2
.setFieldValue("customfield_10050", "Pro"); // Assigning the value for the custom field here
issueInput:
customfield_10050=FieldInput{id=customfield_10806, value=Pro
Error:
RestClientException{statusCode=Optional.of(400), errorCollections=[ErrorCollection{status=400, errors={Change Type=data was not an array}, errorMessages=[]}]}
Attempt 3
.setFieldValue("customfield_10050", ComplexIssueInputFieldValue.with("value", new Object[] {"Pro"})) // Assigning the value for the custom field here
issueInput:
customfield_10806=FieldInput{id=customfield_10806, value=ComplexIssueInputFieldValue{valuesMap={value=[Ljava.lang.Object;#500aca05}}}
Error:
RestClientException{statusCode=Optional.absent(), errorCollections=[]}
I tried almost all the different combinations which I'm aware of. I'm not sure how to proceed further.

How to implement Guava cache to store and get different types of objects?

Right now my cache looks like the following:
public class TestCache {
private LoadingCache<String, List<ObjectABC>> cache;
TestCache() {
cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).maximumSize(25)
.build(new CacheLoader<String, List<ObjectABC>>(
) {
#Override
public List<ObjectABC> load(String key) throws Exception {
// TODO Auto-generated method stub
return addCache(key);
}
});
}
private List<ObjectABC> addCache(String key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key), null, true);
final List<ObjectABC> configsList = new ArrayList<>();
allConfigFiles.forEach(configFile -> {
try {
configsList.add(mapper.readValue(configFile, new TypeReference<ObjectABC>() {
}));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
return configsList;
}
public List<ObjectABC> getEntry(String key) {
try {
return cache.get(key);
} catch (ExecutionException e) {
throw new NonRetriableException(String.format(
"Exception occured while trying to get data from cache for the key : {} Exception: {}",
key.toString(), e));
}
}
}
In the above code, when I pass a String key (which is path to a local folder) it takes all the files present in that location and maps them to ObjectABC using ObjectMapper.
Now my problem is that I want to instead have a generic loading cache like
LoadingCache<String, List<Object>>.
And I want to map files in different folders to different Objects, e.g. map files in /root/Desktop/folder1 to List<ObjectABC> and map files in /root/Desktop/folder2 to List<ObjectDEF> and be able to store and retrieve that information from the cache.
How can I pass to the cache the information of which object to use for mapping?
You can create a custom class wrapping a LoadingCache<Key<?>, Object> like that:
class HeterogeneousCache {
private final LoadingCache<Key<?>, Object> cache;
public <T> T get(Key<T> key) throws ExecutionException {
return key.getType().cast(cache.get(key));
}
}
#Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final Class<T> type;
}
(I used Lombok's #Value annotation for simplicity)
Of course, this is just a stub and you might need to adapt this to your needs. The main problem might be that you can't get a Class<List<ObjectABC>> - you can only get a Class<List>. The easiest way out of this is to wrap the List<ObjectABC> in some custom type. The harder way (not recommended) is to use Guava's TypeToken.
Attribution: This answer is based on the post by Frank Appel entitled How to Map Distinct Value Types Using Java Generics, which itself is based on Joshua Bloch's typesafe hetereogeneous containers from Effective Java.
Edit: A Complete Solution
Since the OP wants List<T> as result, and since he needs instances of TypeReference<T>, I replaced Class<T> with TypeReference<T> in Key<T>:
#Value // provides constructor, getters, equals, hashCode
class Key<T> {
private final String identifier;
private final TypeReference<T> typeReference;
}
Here's how CustomHeterogeneousCache looks now:
class CustomHeterogeneousCache {
private final LoadingCache<Key<?>, List<?>> cache = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.maximumSize(25)
.build(CacheLoader.from(this::computeEntry));
#SuppressWarnings("unchecked")
public <T> List<T> getEntry(Key<T> key) {
return (List<T>) cache.getUnchecked(key);
}
private <T> List<T> computeEntry(Key<T> key) {
final JoiObjectMapper mapper = new JoiObjectMapper();
final Collection<File> allConfigFiles = FileUtils.listFiles(new File(key.getIdentifier()), null, true);
return allConfigFiles.stream()
.map(configFile -> {
try {
return mapper.readValue(configFile, key.getTypeReference());
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
}
}
Since implementations of TypeReference do not have value semantics, the user must make sure that every Key is created once, and then only referenced, e.g.:
class Keys {
public static final Key<ObjectABC> ABC = new Key<>("/root/Desktop/folder1", new TypeReference<ObjectABC>() {
});
public static final Key<ObjectDEF> DEF = new Key<>("/root/Desktop/folder2", new TypeReference<ObjectDEF>() {
});
}

Ways to reduce boilerplate using guava cache

I have dozens of data access objects like PersonDao with methods like:
Person findById(String id) {}
List<Person> search(String firstName, LastName, Page) {}
int searchCount(String firstName, LastName) {}
I've experimented by adding guava cache with one of these classes and it's really nice, but there's a lot of boilerplate.
Here's an example of making findById look in the cache first:
private final LoadingCache<String, Person> cacheById = CacheBuilder.newBuilder()
.maximumSize(maxItemsInCache)
.expireAfterWrite(cacheExpireAfterMinutes, TimeUnit.MINUTES)
.build(new CacheLoader<String, Person>() {
public Person load(String key) {
return findByIdNoCache(key);
});
//.... and update findById to call the cache ...
#Override
public Person findById(String id) {
return cacheById.getUnchecked(id);
}
So, because each method has different params and return types, I end up created a separate cacheLoader for every method!
I tried consolidating everything into a single CacheLoader that returns Object type and accepts a Map of objects, but then I end up with big ugly if/else to figure out which method to call to load the cache.
I'm struggling to find an elegant way to add caching to these data access objects, any suggestions? Maybe guava cache isn't meant for this use case?
Try this. Unfortunately, there are compiler warnings due to generics... But we may supress them because we know nothing bad will happen.
public class CacheContainer {
private static final long maxItemsInCache = 42;
private static final long cacheExpireAfterMinutes = 42;
private final Map<String, LoadingCache> caches = Maps.newHashMap();
public <K, V> V getFromCache(String cacheId, K key, CacheLoader<K, V> loader) throws ExecutionException {
LoadingCache<K, V> cache = caches.get(cacheId);
if (cache == null) {
cache = CacheBuilder.newBuilder().
maximumSize(maxItemsInCache).
expireAfterWrite(cacheExpireAfterMinutes, TimeUnit.MINUTES).
build(loader);
caches.put(cacheId, cache);
}
return cache.get(key);
}
}
And then in your dao:
private final CacheContainer cacheContainer = new CacheContainer();
public Person findById(String id) {
cacheContainer.getFromCache("personById", id, new CacheLoader<String, Person>() {
#Override
public Person load(String key) {
return findByIdNoCache(key);
});
}
Other methods in the same way. I don't think you can reduce boilerplate any more than that.
Creating a CacheLoader (and separate cache) for each method you want to cache the results of is necessary. You could simplify things a little by creating a single CacheBuilder with the cache configuration you want and then creating each of the caches like that:
private final CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder()
.maximumSize(maxItemsInCache)
.expireAfterWrite(cacheExpireAfterMinutes, TimeUnit.MINUTES);
private final LoadingCache<String, Person> cacheById = builder.build(
new CacheLoader<String, Person>() {
// ...
});
private final LoadingCache<Search, List<Person>> searchCache = builder.build(
new CacheLoader<Search, List<Person>>() {
// ...
});
// etc.

I'm getting "Value is not a valid option" for SelectManyCheckBox

I have this code in my JSP page:
<h:selectManyCheckbox id="chb" value="#{MyBean.selectedCheckBoxes}" layout="pageDirection">
<f:selectItems value="#{MyBean.checkBoxItems}"/>
</h:selectManyCheckbox>
And in my MyBean:
public class MyBean {
public MyBean() {
for (Elem section : sections) {
checkBoxItems.put(section.getName(), section.getObjectID());
}
}
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
public String save() {
//save is not being executed....
return FORWARD;
}
public Map<String, Object> getCheckBoxItems() {
return checkBoxItems;
}
public void setCheckBoxItems(Map<String, Object> checkBoxItems) {
this.checkBoxItems = checkBoxItems;
}
public String[] getSelectedCheckBoxes() {
return selectedCheckBoxes;
}
public void setSelectedCheckBoxes(String[] selectedCheckBoxes) {
this.selectedCheckBoxes = selectedCheckBoxes;
}
}
When I click save it is giving the below message in <t:message for="chb"/>
"chb": Value is not a valid option.
Even though I did not add the required attribute for h:selectManyCheckbox, it is trying to validate or doing something else...
I've changed checkBoxItems variable type(with getter/setters) to List<SelectItem>, but it is not working as well.
What can be the reason, how can I solve it?
PS: I'm using JSF 1.1
You will get this error when the equals() test on a selected item has not returned true for any of the available items. So, when roughly the following happens under JSF's covers:
boolean valid = false;
for (Object availableItem : availableItems) {
if (selectedItem.equals(availableItem)) {
valid = true;
break;
}
}
if (!valid) {
// Validation error: Value is not valid!
}
That can in your particular case only mean that section.getObjectID() does not return a String which is what your selectedCheckboxes is declared to, but a different type or a custom type where equals() is not implemented or broken.
Update as per your comment, the getObjectID() returns Integer. It's thus been treated as String because selectedCheckBoxes is declared as String[]. You should change the following
private String[] selectedCheckBoxes;
private Map<String, Object> checkBoxItems = new LinkedHashMap<String, Object>();
to
private Integer[] selectedCheckBoxes;
private Map<String, Integer> checkBoxItems = new LinkedHashMap<String, Integer>();
and maybe (not sure, can't tell from top of head now) also explicitly supply a converter:
<h:selectManyCheckbox ... converter="javax.faces.Integer">
i didnt find any problem in th code, i thought there is the problem the list u passed to oneManyCheckBox.
hardcode some values in list in getter than check
public Map<String, Object> getCheckBoxItems() {
checkBoxItems.clear();
checkBoxItems.put("aaaa", "aaaa");
checkBoxItems.put("bbbb", "bbbb");
checkBoxItems.put("cccc", "cccc");
checkBoxItems.put("dddd", "dddd");
checkBoxItems.put("eeee", "eeee");
return checkBoxItems;
}

How to properly lazy initialize Map of Map of Map?

It may be a bad practice, but I haven't been able to figure out any better solution for my problem. So I have this map
// Map<state, Map<transition, Map<property, value>>>
private Map<String, Map<String, Map<String, String>>> properties;
and I want to initialize it so I don't get NullPointerException with this
properties.get("a").get("b").get("c");
I tried this one but I didn't work (obviously)
properties = new HashMap<String, Map<String, Map<String,String>>>();
Other things I tried didn't compile.
Also if you have any ideas how to avoid this nested maps, I would appreciate it.
It seems to me that you need to create your own Key class:
public class Key {
private final String a;
private final String b;
private final String c;
public Key(String a, String b, String c) {
// initialize all fields here
}
// you need to implement equals and hashcode. Eclipse and IntelliJ can do that for you
}
If you implement your own key class, your map will look like this:
Map<Key, String> map = new HashMap<Key, String>();
And when looking for something in the map you can use:
map.get(new Key("a", "b", "c"));
The method above will not throw a NullPointerException.
Please remember that for this solution to work, you need to override equals and hashcode in the Key class. There is help here. If you don't override equals and hashcode, then a new key with the same elements won't match an existing key in the map.
There are other possible solutions but implementing your own key is a pretty clean one in my opinion. If you don't want to use the constructor you can initialize your key with a static method and use something like:
Key.build(a, b, c)
It is up to you.
You need to put maps in your maps in your map. Literally:
properties = new HashMap<String, Map<String, Map<String,String>>>();
properties.put("a", new HashMap<String, Map<String,String>>());
properites.get("a").put("b", new HashMap<String,String>());
If your target is lazy initialization without NPE you have to create your own map:
private static abstract class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val == null && key instanceof K) {
put((K)key, val = create());
}
return val;
}
protected abstract V create();
}
public void initialize() {
properties = new MyMap<String, Map<String, Map<String, String>>>() {
#Override
protected Map<String, Map<String, String>> create() {
return new MyMap<String, Map<String, String>>() {
#Override
protected Map<String, String> create() {
return new HashMap<String, String>();
}
};
}
};
}
You could use a utility method:
public static <T> T get(Map<?, ?> properties, Object... keys) {
Map<?, ?> nestedMap = properties;
for (int i = 0; i < keys.length; i++) {
if (i == keys.length - 1) {
#SuppressWarnings("unchecked")
T value = (T) nestedMap.get(keys[i]);
return value;
} else {
nestedMap = (Map<?, ?>) nestedMap.get(keys[i]);
if(nestedMap == null) {
return null;
}
}
}
return null;
}
This can be invoked like this:
String result = get(properties, "a", "b", "c");
Note that care is required when using this as it is not type-safe.
The only way to do it with this structure is to pre-initialise the 1st and 2nd level maps with ALL possible keys. If this is not possible to do you can't achieve what you are asking with plain Maps.
As an alternative you can build a custom data structure that is more forgiving. For example a common trick is for a failed key lookup to return an "empty" structure rather than null, allowing nested access.
You can't initialize this in one go, since you normally don't know what keys you'll have in advance.
Thus you'd have to check whether the submap for a key is null and if so you might add an empty map for that. Preferably you'd only do that when adding entries to the map and upon retrieving entries you return null if one of the submaps in the path doesn't exist. You could wrap that in your own map implementation for ease of use.
As an alternative, apache commons collections' MultiKeyMap might provide what you want.
It's impossible to use properties.get("a").get("b").get("c"); and be sure to avoid null unless you make your own Map. In fact, you can't predict that your map will contains "b" key.
So try to make your own class to handle nested get.
I think a better solution is using an object as the only key to the map of values. The key will be composed of three fields, state, transition and property.
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Key {
private String state;
private String transition;
private String property;
public Key(String state, String transition, String property) {
this.state = state;
this.transition = transition;
this.property = property;
}
#Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
When you check for a value, the map will return null for a key that is not associated with a value
Map<Key, String> values = new HashMap<Key, String>();
assert values.get(new Key("a", "b", "c")) == null;
values.put(new Key("a", "b", "c"), "value");
assert values.get(new Key("a", "b", "c")) != null;
assert values.get(new Key("a", "b", "c")).equals("value");
To efficiently and correctly use an object as a key in a Map you should override the methods equals() and hashCode(). I have built thos methods using the reflective functionalities of the Commons Lang library.
I think, following is the easier way:
public static final Map<Integer, Map<Integer, Map<Integer, Double>>> A_Map = new HashMap<Integer, Map<Integer, Map<Integer, Double>>>()
{
{
put(0, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 60.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 160.0);
put(1, 1 / 13600.0);
}
});
}
});
put(1, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 260.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 560.0);
put(1, 1 / 1300.0);
}
});
}
});
}
};
Using computeIfAbsent/putIfAbsent makes it simple:
private <T> void addValueToMap(String keyA, String keyB, String keyC, String value) {
map.computeIfAbsent(keyA, k -> new HashMap<>())
.computeIfAbsent(keyB, k -> new HashMap<>())
.putIfAbsent(keyC, value);
}

Categories