How can I achieve more optimization in following code?, I don't like my last check "Objects.isNull" ? Thanks in advance.
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
AtomicReference<ServiceRequestDAO> atomicReference = new AtomicReference<>(serviceRequestDAO);
final ItemHolder itemHolder = Optional.ofNullable(itemHolderParameter).orElseThrow(() -> new IllegalArgumentException("ItemHolder must not be null"));
Optional.ofNullable(itemHolder.getServiceRequestDAO()).ifPresent(atomicReference::set);
//Final validation.
final ServiceRequestDAO requestDAO = atomicReference.get();
if(Objects.isNull(requestDAO)){
throw new CancellationException("Unable to get ServiceRequestDAO (null)");
}
return requestDAO;
}
~M
There seems to be no good reason to bring Optional or AtomicReference into it:
if (itemHolderParameter == null) {
throw new IllegalArgumentException(...)
}
ServiceRequestDao dao = itemHolder.getServiceRequestDAO();
if (dao != null) {
return dao;
}
if (serviceRequestDao != null) {
return serviceRequestDao;
}
throw new CancellableException(...);
I have done my best to follow the semantics of your code, but honestly, it is very unclear.
Thanks Boris,
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
return Optional.ofNullable(Optional.ofNullable(itemHolderParameter).map(ItemHolder::getServiceRequestDAO).orElse(serviceRequestDAO)).orElseThrow(() -> new CancellationException("No service request could be retrieved."));
}
The test cases:
public class HolderUtilsTest {
#Test(expected = CancellationException.class)
public void testHolderParameterNulls(){
HolderUtils.getServiceRequestDAO(null, null);
}
#Test(expected = CancellationException.class)
public void testHolderParameterHolderNull(){
HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), null);
}
#Test
public void testHolderParameterNullAndSRNotNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), serviceRequestDAO);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderParameterNotNullWithObjectAndSRNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO).build(), null);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderBothPresent(){
final ServiceRequestDAO serviceRequestDAO0 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAOResult = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO0).build(), serviceRequestDAO1);
assertThat(serviceRequestDAOResult).isNotNull();
assertThat(serviceRequestDAOResult).isEqualTo(serviceRequestDAO0);
assertThat(serviceRequestDAOResult).isNotEqualTo(serviceRequestDAO1);
}
Related
The expiration time of a JWT can be set by configuring micronaut.security.token.jwt.generator.access-token-expiration.
Is it possible to have a custom value for individually issued JWT tokens? Searching the documentation I haven't found any useful information except you can replace the BearerTokenRenderer and return a custom response.
public AccessRefreshToken render(Integer expiresIn, String accessToken, #Nullable String refreshToken) {
return new AccessRefreshToken(accessToken, refreshToken, BEARER_TOKEN_TYPE, ***customValue**);
}
Will returning a different value for expires_in work in this case?
According to micronaut documentation - pt 9.3.5
You can replace ClaimGenerator with your own:
ClaimGenerator
Which has a method with parameter Integer expiration
You can replace classes by using #Replaces annotation like described here:
https://micronaut-projects.github.io/micronaut-core/latest/guide/#replaces
I hope that solves your issue
The solution is to extend the JwTClaimsSetGenerator
#Replaces(JWTClaimsSetGenerator.class)
#Singleton
public class CustomClaimsGenerator extends JWTClaimsSetGenerator {
private static final Logger LOG = LoggerFactory.getLogger(CustomClaimsGenerator.class);
private static final String ROLES_KEY = "rolesKey";
private final TokenConfiguration tokenConfiguration;
private final JwtIdGenerator jwtIdGenerator;
private final ClaimsAudienceProvider claimsAudienceProvider;
private final String appName;
/**
* #param tokenConfiguration Token Configuration
* #param jwtIdGenerator Generator which creates unique JWT ID
* #param claimsAudienceProvider Provider which identifies the recipients that the JWT is intended for.
* #param applicationConfiguration The application configuration
*/
public CustomClaimsGenerator(TokenConfiguration tokenConfiguration,#Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider, ApplicationConfiguration applicationConfiguration) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration);
this.tokenConfiguration = tokenConfiguration;
this.jwtIdGenerator = jwtIdGenerator;
this.claimsAudienceProvider = claimsAudienceProvider;
this.appName = applicationConfiguration != null ? applicationConfiguration.getName().orElse(Environment.MICRONAUT) : Environment.MICRONAUT;
}
/**
* #param authentication Authenticated user's representation.
* #param expiration expiration time in seconds
* #return The authentication claims
*/
#Override
public Map<String, Object> generateClaims(Authentication authentication, #Nullable Integer expiration) {
expiration = 1000; //works with this value
JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
populateIat(builder);
populateExp(builder, 15);
populateJti(builder);
populateIss(builder);
populateAud(builder);
populateNbf(builder);
populateWithAuthentication(builder, authentication);
if (LOG.isDebugEnabled()) {
LOG.debug("Generated claim set: {}", builder.build().toJSONObject());
}
return builder.build().getClaims();
}
/**
* Populates iss claim.
*
* #param builder The Claims Builder
* #see iss (Issuer) Claim
*/
protected void populateIss(JWTClaimsSet.Builder builder) {
if (appName != null) {
builder.issuer(appName); // iss
}
}
/**
* Populates sub claim.
*
* #param builder The Claims Builder
* #param authentication Authenticated user's representation.
* #see sub (Subject) Claim
*/
protected void populateSub(JWTClaimsSet.Builder builder, Authentication authentication) {
builder.subject(authentication.getName()); // sub
}
/**
* Populates aud claim.
*
* #param builder The Claims Builder
* #see aud (Audience) Claim
*/
protected void populateAud(JWTClaimsSet.Builder builder) {
if (claimsAudienceProvider != null) {
builder.audience(claimsAudienceProvider.audience()); // aud
}
}
/**
* Populates exp claim.
*
* #param builder The Claims Builder
* #param expiration expiration time in seconds
* #see exp (ExpirationTime) Claim
*/
protected void populateExp(JWTClaimsSet.Builder builder, #Nullable Integer expiration) {
if (expiration != null) {
LOG.debug("Setting expiration to {}", expiration);
System.out.print(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS)));
builder.expirationTime(Date.from(Instant.now().plus(expiration, ChronoUnit.SECONDS))); // exp
}
}
/**
* Populates nbf claim.
*
* #param builder The Claims Builder
* #see nbf (Not Before) Claim
*/
protected void populateNbf(JWTClaimsSet.Builder builder) {
builder.notBeforeTime(new Date()); // nbf
}
/**
* Populates iat claim.
*
* #param builder The Claims Builder
* #see iat (Issued At) Claim
*/
protected void populateIat(JWTClaimsSet.Builder builder) {
builder.issueTime(new Date()); // iat
}
/**
* Populates jti claim.
*
* #param builder The Claims Builder
* #see jti (JWT ID) Claim
*/
protected void populateJti(JWTClaimsSet.Builder builder) {
if (jwtIdGenerator != null) {
builder.jwtID(jwtIdGenerator.generateJtiClaim()); // jti
}
}
/**
* Populates Claims with Authentication object.
*
* #param builder the Claims Builder
* #param authentication Authenticated user's representation.
*/
protected void populateWithAuthentication(JWTClaimsSet.Builder builder, Authentication authentication) {
populateSub(builder, authentication);
authentication.getAttributes().forEach(builder::claim);
String rolesKey = tokenConfiguration.getRolesName();
if (!rolesKey.equalsIgnoreCase(TokenConfiguration.DEFAULT_ROLES_NAME)) {
builder.claim(ROLES_KEY, rolesKey);
}
builder.claim(rolesKey, authentication.getRoles());
}
/**
* #param oldClaims The old claims to use as a base in the new token generation.
* #param expiration expiration time in seconds
* #return Instance of {#link JWTClaimsSet}
*/
#Override
public Map<String, Object> generateClaimsSet(Map<String, ?> oldClaims, Integer expiration) {
JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder();
List<String> excludedClaims = Arrays.asList(JwtClaims.EXPIRATION_TIME, JwtClaims.ISSUED_AT, JwtClaims.NOT_BEFORE);
for (String k : oldClaims.keySet()
.stream()
.filter(p -> !excludedClaims.contains(p))
.collect(Collectors.toList())) {
builder.claim(k, oldClaims.get(k));
}
populateExp(builder, expiration);
populateIat(builder);
populateNbf(builder);
return builder.build().getClaims();
}
}
I am making a network request with Volley and I am using but which was working very well until suddenly it started throwing Null pointer exception on the requestQueue.
class VolleySingleton(context: Context) {
companion object{
#Volatile
private var newInstance: VolleySingleton? = null
fun getInstance(context: Context) =
newInstance
?: synchronized(this){
newInstance
?: VolleySingleton(context).also{
newInstance = it
}
}
}
private val requestQueue: RequestQueue by lazy{
Volley.newRequestQueue(context) // throws NullPointer exception
}
fun<T> addToRequestQueue(req: Request<T>){
requestQueue.add(req)
}
}
I have tried to initialize it like this
private val requestQueue: RequestQueue =
Volley.newRequestQueue(context) // throws NullPointer exception
but it won't work still.
You should use context.getApplicationContext() instead of context.
// Add a request (in this example, called stringRequest) to your RequestQueue.
VolleySingleton.getInstance(context.getApplicationContext()).addToRequestQueue(request)
I do this in the Application class which is global and stays in scope for the life of the app session. This is in Java, but you might see something I'm doing that you are not.
public class MyApplication extends Application {
/**
* Log or request TAG
*/
public static final String TAG = "MyApp";
/**
* Global request queue for Volley
*/
private RequestQueue mRequestQueue;
/**
* A singleton instance of the application class for easy access in other places
*/
private static MyApplication sInstance;
#Override
public void onCreate() {
super.onCreate();
// initialize the singleton
sInstance = this;
}
/**
* #return MyApplication singleton instance
*/
public static synchronized MyApplication getInstance() {
return sInstance;
}
/**
* #return The Volley Request queue, the queue will be created if it is null
*/
public RequestQueue getRequestQueue() {
// lazy initialize the request queue, the queue instance will be
// created when it is accessed for the first time
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
/**
* Adds the specified request to the global queue, if tag is specified
* then it is used else Default TAG is used.
*
* #param req
* #param tag
*/
public <T> void addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
VolleyLog.d("Adding request to queue: %s", req.getUrl());
getRequestQueue().add(req);
}
/**
* Adds the specified request to the global queue using the Default TAG.
*
* #param req
* #param tag
*/
public <T> void addToRequestQueue(Request<T> req) {
// set the default tag if tag is empty
req.setTag(TAG);
getRequestQueue().add(req);
}
/**
* Cancels all pending requests by the specified TAG, it is important
* to specify a TAG so that the pending/ongoing requests can be cancelled.
*
* #param tag
*/
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
Then, you call this in your other Activity code after creating your request object:
MyApplication.getInstance().addToRequestQueue(postRequest);
The unit tests (JUnit, Mockito) that I've written for my Caffeine CacheLoader implementation all succeed when I run them individually, but one of them fails when I run them all together. I believe that I am following best practices in using #Before for all of my test object setups.
When run with the others, the test testGet_WhenCalledASecondAndThirdTimeBeyondCacheDuration_LoadingMethodCalledASecondTime fails every time with the following error:
org.mockito.exceptions.verification.TooLittleActualInvocations:
testDataSource.getObjectWithKey(
"mountain-bikes"
);
Wanted 2 times:
-> at ErrorHandlingLoadingCacheFactoryTest.testGet_WhenCalledASecondAndThirdTimeBeyondCacheDuration_LoadingMethodCalledASecondTime(ErrorHandlingLoadingCacheFactoryTest.java:67)
But was 1 time:
-> at ErrorHandlingCacheLoader.load(ErrorHandlingCacheLoader.java:41)
Something would appear to be carrying over between the tests, but given what I am doing in my #Before method I'm not sure how that could be. I've tried calling the following in an #After method:
invalidateAll()
cleanUp()
Mockito.reset(testDataSource)
I have also tried to manually pass a singleThreadExecutor to the cache builder, and waiting for it to finish whatever it is doing in #After in case that has anything to do with it.
My Caffeine CacheLoader implementation just overrides the reload method to return the currently cached value if the attempt to refresh it fails (throws an exception). Other than that, its pretty vanilla.
#Component
public class ErrorHandlingLoadingCacheFactory {
private final Ticker ticker;
#Autowired
public ErrorHandlingLoadingCacheFactory(Ticker ticker) {
this.ticker = ticker;
}
public <T> LoadingCache<String, T> buildCache(String cacheName,
long duration,
TimeUnit timeUnit,
Function<String, T> valueResolver) {
return Caffeine.newBuilder()
.refreshAfterWrite(duration, timeUnit)
.ticker(ticker)
.build(new ErrorHandlingCacheLoader<>(cacheName, valueResolver));
}
}
/**
* a LoadingCache that retains stale cache values if
* an attempt to retrieve a fresh value for a given key fails.
*
* #param <K> the cache key type
* #param <V> the cache value type
*/
class ErrorHandlingCacheLoader<K, V> implements CacheLoader<K, V> {
private final static Logger logger = LoggerFactory.getLogger(ErrorHandlingCacheLoader.class);
private final String cacheName;
private final Function<K, V> valueResolver;
/**
* Create a cache.
*
* #param cacheName the cache name
* #param valueResolver the method used to get a value for a key
*/
public ErrorHandlingCacheLoader(String cacheName, Function<K, V> valueResolver) {
this.cacheName = cacheName;
this.valueResolver = valueResolver;
}
/**
* Load the initial cache value for a given key.
* #param key the cache key
* #return the initial value to cache
*/
#Override
public V load(#NonNull K key) {
return valueResolver.apply(key);
}
/**
* Attempt to reload a value for a given key.
* #param key the cache key
* #param oldValue the currently cached value for the given key
* #return
*/
#Override
public V reload(#NonNull K key, V oldValue) {
V value = oldValue;
try {
value = valueResolver.apply(key);
} catch (RuntimeException e) {
logger.warn("Failed to retrieve value for key '{}' in cache '{}'. Returning currently cached value '{}'.", key, cacheName, oldValue);
}
return value;
}
}
public class ErrorHandlingLoadingCacheFactoryTest {
private ErrorHandlingLoadingCacheFactory errorHandlingLoadingCacheFactory;
private FakeTicker fakeTicker;
private TestDataSource testDataSource;
private LoadingCache<String, TestObject> loadingCache;
#Before
public void setUp() {
fakeTicker = new FakeTicker();
testDataSource = mock(TestDataSource.class);
errorHandlingLoadingCacheFactory = new ErrorHandlingLoadingCacheFactory(fakeTicker::read);
loadingCache = errorHandlingLoadingCacheFactory.buildCache("testCache", 1, TimeUnit.HOURS, testDataSource::getObjectWithKey);
}
#After
public void tearDown() {
validateMockitoUsage();
}
#Test
public void testGet_WhenCalledTwiceWithinCachePeriod_LoadingMethodCalledOnce() {
// Arrange
TestObject testObject = new TestObject("Mountain Bikes");
when(testDataSource.getObjectWithKey("mountain-bikes")).thenReturn(testObject);
// Act
TestObject result1 = loadingCache.get("mountain-bikes");
TestObject result2 = loadingCache.get("mountain-bikes");
// Assert
verify(testDataSource, times(1)).getObjectWithKey("mountain-bikes");
assertThat(result1).isEqualTo(testObject);
assertThat(result2).isEqualTo(testObject);
}
#Test
public void testGet_WhenCalledASecondAndThirdTimeBeyondCacheDuration_LoadingMethodCalledASecondTime() {
// Arrange
TestObject testObject1 = new TestObject("Mountain Bikes 1");
TestObject testObject2 = new TestObject("Mountain Bikes 2");
when(testDataSource.getObjectWithKey("mountain-bikes")).thenReturn(testObject1, testObject2);
// Act
TestObject result1 = loadingCache.get("mountain-bikes");
fakeTicker.advance(2, TimeUnit.HOURS);
TestObject result2 = loadingCache.get("mountain-bikes");
TestObject result3 = loadingCache.get("mountain-bikes");
// Assert
verify(testDataSource, times(2)).getObjectWithKey("mountain-bikes");
assertThat(result1).isEqualTo(testObject1);
assertThat(result2).isEqualTo(testObject1);
assertThat(result3).isEqualTo(testObject2);
}
#Test(expected = RuntimeException.class)
public void testGet_WhenFirstLoadCallThrowsRuntimeException_ThrowsRuntimeException() {
// Arrange
when(testDataSource.getObjectWithKey("mountain-bikes")).thenThrow(new RuntimeException());
// Act
loadingCache.get("mountain-bikes");
}
#Test
public void testGet_WhenFirstLoadCallSuccessfulButSecondThrowsRuntimeException_ReturnsCachedValueFromFirstCall() {
// Arrange
TestObject testObject1 = new TestObject("Mountain Bikes 1");
when(testDataSource.getObjectWithKey("mountain-bikes")).thenReturn(testObject1).thenThrow(new RuntimeException());
// Act
TestObject result1 = loadingCache.get("mountain-bikes");
fakeTicker.advance(2, TimeUnit.HOURS);
TestObject result2 = loadingCache.get("mountain-bikes");
// Assert
verify(testDataSource, times(2)).getObjectWithKey("mountain-bikes");
assertThat(result1).isEqualTo(testObject1);
assertThat(result2).isEqualTo(testObject1);
}
#Test
public void testGet_WhenFirstLoadCallSuccessfulButSecondThrowsRuntimeException_SubsequentGetsReturnCachedValueFromFirstCall() {
// Arrange
TestObject testObject1 = new TestObject("Mountain Bikes 1");
when(testDataSource.getObjectWithKey("mountain-bikes")).thenReturn(testObject1).thenThrow(new RuntimeException());
// Act
TestObject result1 = loadingCache.get("mountain-bikes");
fakeTicker.advance(2, TimeUnit.HOURS);
TestObject result2 = loadingCache.get("mountain-bikes");
TestObject result3 = loadingCache.get("mountain-bikes");
// Assert
verify(testDataSource, times(2)).getObjectWithKey("mountain-bikes");
assertThat(result1).isEqualTo(testObject1);
assertThat(result2).isEqualTo(testObject1);
assertThat(result3).isEqualTo(testObject1);
}
#Test(expected = NullPointerException.class)
public void testGet_WhenKeyIsNull_ThrowsNullPointerException() {
// Arrange
String key = null;
// Act
loadingCache.get(key);
}
#Test
public void testGet_WhenFirstLoadCallReturnsNull_DoesNotCacheResult() {
// Arrange
TestObject testObject1 = new TestObject("Mountain Bikes 1");
when(testDataSource.getObjectWithKey("mountain-bikes")).thenReturn(null).thenReturn(testObject1);
// Act
TestObject result1 = loadingCache.get("mountain-bikes");
TestObject result2 = loadingCache.get("mountain-bikes");
// Assert
verify(testDataSource, times(2)).getObjectWithKey("mountain-bikes");
assertThat(result1).isEqualTo(null);
assertThat(result2).isEqualTo(testObject1);
}
#Data
class TestObject {
private String id;
public TestObject(String id) {
this.id = id;
}
}
interface TestDataSource {
TestObject getObjectWithKey(String key);
}
}
Ben Manes suggested in his comment that I use Runnable::run as the LoadingCache's executor when running unit tests, which did the trick!
I implemented a second protected buildCache method on my factory that additionally takes an Executor parameter, which my test class uses to pass Runnable::run.
The updated ErrorHandlingLoadingCacheFactory:
public class ErrorHandlingLoadingCacheFactory {
private final Ticker ticker;
#Autowired
public ErrorHandlingLoadingCacheFactory(Ticker ticker) {
this.ticker = ticker;
}
/**
* Create an in-memory LoadingCache
*
* #param cacheName the name of the cache
* #param duration how long to keep values in the cache before attempting to refresh them
* #param timeUnit the unit of time of the given duration
* #param valueResolver the method to call to get a value to load into the cache for a given key
* #param <T> the type of object to store into the cache
* #return the newly created cache
*/
public <T> LoadingCache<String, T> buildCache(String cacheName,
long duration,
TimeUnit timeUnit,
Function<String, T> valueResolver) {
return buildCache(cacheName, duration, timeUnit, valueResolver, ForkJoinPool.commonPool());
}
/**
* Create an in-memory LoadingCache
*
* #param cacheName the name of the cache
* #param duration how long to keep values in the cache before attempting to refresh them
* #param timeUnit the unit of time of the given duration
* #param valueResolver the method to call to get a value to load into the cache for a given key
* #param executor the executor for the cache to use
* #param <T> the type of object to store into the cache
* #return the newly created cache
*/
protected <T> LoadingCache<String, T> buildCache(String cacheName,
long duration,
TimeUnit timeUnit,
Function<String, T> valueResolver,
Executor executor) {
return Caffeine.newBuilder()
.refreshAfterWrite(duration, timeUnit)
.ticker(ticker)
.executor(executor)
.build(new ErrorHandlingCacheLoader<>(cacheName, valueResolver));
}
}
the updated setUp() method in ErrorHandlingLoadingCacheFactoryTest:
...
#Before
public void setUp() {
fakeTicker = new FakeTicker();
testDataSource = mock(TestDataSource.class);
errorHandlingLoadingCacheFactory = new ErrorHandlingLoadingCacheFactory(fakeTicker::read);
loadingCache = errorHandlingLoadingCacheFactory.buildCache("testCache", 1, TimeUnit.HOURS, testDataSource::getObjectWithKey, Runnable::run);
}
...
There must have been a race between my tests that my single-threaded executor didn't catch, probably because I didn't properly wait for it to terminate in my #After method. Ben suggested that if I used awaitTermination on the single-threaded executor, that might also have worked.
I am testing my DTOs using a generic dto testing code that I found online https://objectpartners.com/2016/02/16/automatically-junit-test-dto-and-transfer-objects/. Most of the time, it works perfect. Suddenly one day, I got an test failure for just one test class. And it is occasionally occurred, which means sometime it happened, most of the time, it works fine.
pubic class MyDtoClass implements Serializable {
private String includeNullValue;
public String getIncludeNullValue() {
return includeNullValue;
}
public void setIncludeNullValue(String includeNullValue) {
this.includeNullValue = includeNullValue;
}
}
The test output the following error message when it failed:
java.lang.AssertionError: includeNullValue is different expected same:<> was not:<null>
Expected :
Actual :null
My test class is as below
public class MyDtoClassTest extends DtoTest<MyDtoClass> {
private static MyDtoClass myDtoClass;
#Before
public void setup() {
myDtoClass = new MyDtoClass();
}
#Override
protected MyDtoClass getInstance() {
return myDtoClass;
}
}
Any clue? Thanks in advance.
UPDATE: As suggested, I directly post the DtoTest.java here as below
public abstract class DtoTest<T> {
/** A map of default mappers for common objects. */
private static final ImmutableMap<Class<?>, Supplier<?>> DEFAULT_MAPPERS;
static {
final Builder<Class<?>, Supplier<?>> mapperBuilder = ImmutableMap.builder();
/* Primitives */
mapperBuilder.put(int.class, () -> 0);
mapperBuilder.put(double.class, () -> 0.0d);
mapperBuilder.put(float.class, () -> 0.0f);
mapperBuilder.put(long.class, () -> 0l);
mapperBuilder.put(boolean.class, () -> true);
mapperBuilder.put(short.class, () -> (short) 0);
mapperBuilder.put(byte.class, () -> (byte) 0);
mapperBuilder.put(char.class, () -> (char) 0);
mapperBuilder.put(Integer.class, () -> Integer.valueOf(0));
mapperBuilder.put(Double.class, () -> Double.valueOf(0.0));
mapperBuilder.put(Float.class, () -> Float.valueOf(0.0f));
mapperBuilder.put(Long.class, () -> Long.valueOf(0));
mapperBuilder.put(Boolean.class, () -> Boolean.TRUE);
mapperBuilder.put(Short.class, () -> Short.valueOf((short) 0));
mapperBuilder.put(Byte.class, () -> Byte.valueOf((byte) 0));
mapperBuilder.put(Character.class, () -> Character.valueOf((char) 0));
mapperBuilder.put(BigDecimal.class, () -> BigDecimal.ONE);
mapperBuilder.put(Date.class, () -> new Date());
/* Collection Types. */
mapperBuilder.put(Set.class, () -> Collections.emptySet());
mapperBuilder.put(SortedSet.class, () -> Collections.emptySortedSet());
mapperBuilder.put(List.class, () -> Collections.emptyList());
mapperBuilder.put(Map.class, () -> Collections.emptyMap());
mapperBuilder.put(SortedMap.class, () -> Collections.emptySortedMap());
DEFAULT_MAPPERS = mapperBuilder.build();
}
/** The get fields to ignore and not try to test. */
private final Set<String> ignoredGetFields;
/**
* A custom mapper. Normally used when the test class has abstract objects.
*/
private final ImmutableMap<Class<?>, Supplier<?>> mappers;
/**
* Creates an instance of {#link DtoTest} with the default ignore fields.
*/
protected DtoTest() {
this(null, null);
}
/**
* Creates an instance of {#link DtoTest} with ignore fields and additional custom mappers.
*
* #param customMappers Any custom mappers for a given class type.
* #param ignoreFields The getters which should be ignored (e.g., "getId" or "isActive").
*/
protected DtoTest(Map<Class<?>, Supplier<?>> customMappers, Set<String> ignoreFields) {
this.ignoredGetFields = new HashSet<>();
if (ignoreFields != null) {
this.ignoredGetFields.addAll(ignoreFields);
}
this.ignoredGetFields.add("getClass");
if (customMappers == null) {
this.mappers = DEFAULT_MAPPERS;
} else {
final Builder<Class<?>, Supplier<?>> builder = ImmutableMap.builder();
builder.putAll(customMappers);
builder.putAll(DEFAULT_MAPPERS);
this.mappers = builder.build();
}
}
/**
* Calls a getter and verifies the result is what is expected.
*
* #param fieldName The field name (used for error messages).
* #param getter The get {#link Method}.
* #param instance The test instance.
* #param expected The expected result.
*
* #throws IllegalAccessException if this Method object is enforcing Java language access control and the underlying
* method is inaccessible.
* #throws IllegalArgumentException if the method is an instance method and the specified object argument is not an
* instance of the class or interface declaring the underlying method (or of a subclass or implementor
* thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for
* primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to
* the corresponding formal parameter type by a method invocation conversion.
* #throws InvocationTargetException if the underlying method throws an exception.
*/
private void callGetter(String fieldName, Method getter, T instance, Object expected)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
final Object getResult = getter.invoke(instance);
if (getter.getReturnType().isPrimitive()) {
/* Calling assetEquals() here due to autoboxing of primitive to object type. */
assertEquals(fieldName + " is different", expected, getResult);
} else {
/* This is a normal object. The object passed in should be the exactly same object we get back. */
assertSame(fieldName + " is different", expected, getResult);
}
}
/**
* Creates an object for the given {#link Class}.
*
* #param fieldName The name of the field.
* #param clazz The {#link Class} type to create.
*
* #return A new instance for the given {#link Class}.
*
* #throws InstantiationException If this Class represents an abstract class, an interface, an array class, a
* primitive type, or void; or if the class has no nullary constructor; or if the instantiation fails
* for some other reason.
* #throws IllegalAccessException If the class or its nullary constructor is not accessible.
*
*/
private Object createObject(String fieldName, Class<?> clazz)
throws InstantiationException, IllegalAccessException {
try {
final Supplier<?> supplier = this.mappers.get(clazz);
if (supplier != null) {
return supplier.get();
}
if (clazz.isEnum()) {
return clazz.getEnumConstants()[0];
}
return clazz.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new RuntimeException("Unable to create objects for field '" + fieldName + "'.", e);
}
}
/**
* Returns an instance to use to test the get and set methods.
*
* #return An instance to use to test the get and set methods.
*/
protected abstract T getInstance();
/**
* Tests all the getters and setters. Verifies that when a set method is called, that the get method returns the
* same thing. This will also use reflection to set the field if no setter exists (mainly used for user immutable
* entities but Hibernate normally populates).
*
* #throws Exception If an expected error occurs.
*/
#Test
public void testGettersAndSetters() throws Exception {
/* Sort items for consistent test runs. */
final SortedMap<String, GetterSetterPair> getterSetterMapping = new TreeMap<>();
final T instance = getInstance();
for (final Method method : instance.getClass().getMethods()) {
final String methodName = method.getName();
if (this.ignoredGetFields.contains(methodName)) {
continue;
}
String objectName;
if (methodName.startsWith("get") && method.getParameters().length == 0) {
/* Found the get method. */
objectName = methodName.substring("get".length());
GetterSetterPair getterSettingPair = getterSetterMapping.get(objectName);
if (getterSettingPair == null) {
getterSettingPair = new GetterSetterPair();
getterSetterMapping.put(objectName, getterSettingPair);
}
getterSettingPair.setGetter(method);
} else if (methodName.startsWith("set") && method.getParameters().length == 1) {
/* Found the set method. */
objectName = methodName.substring("set".length());
GetterSetterPair getterSettingPair = getterSetterMapping.get(objectName);
if (getterSettingPair == null) {
getterSettingPair = new GetterSetterPair();
getterSetterMapping.put(objectName, getterSettingPair);
}
getterSettingPair.setSetter(method);
} else if (methodName.startsWith("is") && method.getParameters().length == 0) {
/* Found the is method, which really is a get method. */
objectName = methodName.substring("is".length());
GetterSetterPair getterSettingPair = getterSetterMapping.get(objectName);
if (getterSettingPair == null) {
getterSettingPair = new GetterSetterPair();
getterSetterMapping.put(objectName, getterSettingPair);
}
getterSettingPair.setGetter(method);
}
}
/*
* Found all our mappings. Now call the getter and setter or set the field via reflection and call the getting
* it doesn't have a setter.
*/
for (final Entry<String, GetterSetterPair> entry : getterSetterMapping.entrySet()) {
final GetterSetterPair pair = entry.getValue();
final String objectName = entry.getKey();
final String fieldName = objectName.substring(0, 1).toLowerCase() + objectName.substring(1);
if (pair.hasGetterAndSetter()) {
/* Create an object. */
final Class<?> parameterType = pair.getSetter().getParameterTypes()[0];
final Object newObject = createObject(fieldName, parameterType);
pair.getSetter().invoke(instance, newObject);
callGetter(fieldName, pair.getGetter(), instance, newObject);
} else if (pair.getGetter() != null) {
/*
* Object is immutable (no setter but Hibernate or something else sets it via reflection). Use
* reflection to set object and verify that same object is returned when calling the getter.
*/
final Object newObject = createObject(fieldName, pair.getGetter().getReturnType());
final Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, newObject);
callGetter(fieldName, pair.getGetter(), instance, newObject);
}
}
}
}
IMHO is problem with anotation #Before on setup method
Annotating a public void method with #Before causes that method to be run before the Test method
But in your code you don't have any #Test method so object myDtoClass may not be initialize befero test.
Change code to
public class MyDtoClassTest extends DtoTest<MyDtoClass> {
#Override
protected MyDtoClass getInstance() {
return new MyDtoClass();
}
}
I have a question regarding static method access. I have a class within i have 4 static method. as shown in code:
package com.itrucking.util;
public class ZKUtil implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
* #author Shekhar
* #param _class
* #param listbox
* To make Listbox sorting enabled
* #throws NoSuchMethodException
* #throws SecurityException
*/
public static void setSortingEnabled(Class<?> _class, Listbox listbox){
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
sortingPair = getMapForSorting(_class, listbox);
if (!sortingPair.isEmpty()) {
for (Map.Entry<Listheader, String> entry : sortingPair.entrySet()) {
entry.getKey().setSortAscending(
new FieldComparator(entry.getValue(), true));
entry.getKey().setSortDescending(
new FieldComparator(entry.getValue(), false));
}
}
}
/**
* #author Shekhar
* #param _class
* #param listbox
* #return Map<Listheader, String>
*/
private static Map<Listheader, String> getMapForSorting(Class<?> _class,Listbox listbox) {
List<Listheader> headerList = getListHeaderList(listbox);
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
System.out.println(_class);
Field[] fields = _class.getDeclaredFields();
for (Field f : fields) {
// System.out.println(f.getName()+":"+f.getType());
for (Listheader lh : headerList) {
if (f.getName().equals(getId(lh)))
sortingPair.put(lh, f.getName());
}
}
System.out.println(sortingPair);
return sortingPair;
}
private static String getId(Listheader listheader) {
String listheaderId = null;
if (listheader.getId().contains("_")) {
listheaderId = listheader.getId().split("_")[1];
// System.out.println("listheaderId->"+listheaderId);
}
return listheaderId;
}
/**
* #author Shekhar
* #param listbox
* #return List<Listheader>
*/
#SuppressWarnings("unchecked")
private static List<Listheader> getListHeaderList(Listbox listbox) {
List<Listheader> headerList = new ArrayList<Listheader>();
Listhead listhead = null;
List<Component> listboxComponentList = listbox.getChildren();
for (Component listboxComponent : listboxComponentList) {
if (listboxComponent instanceof Listhead) {
listhead = (Listhead) listboxComponent;
break;
}
}
List<Component> listOfComp = listhead.getChildren();
if (listhead != null) {
for (Component c : listOfComp) {
if (c instanceof Listheader)
headerList.add((Listheader) c);
}
}
return headerList;
}
}
and i am calling setSortingEnabled() method from onLoadShipperDetailsListCtrl() from code bellow :
package com.itrucking.webui.controller;
public class ShipperDetailsListCtrl{
/**
* #param e
* #return void
*/
public void onCreate$window_shipperDetailsList(Event e){
onLoadShipperDetailsListCtrl();
}
/**
* #return void
*/
public void onLoadShipperDetailsListCtrl(){
System.out.println("onLoadShipperDetailsListCtrl called.");
shipperList = shipperService.getShipperList();
doRenderListboxShipperDetailsList(shipperList);
ZKUtil.setSortingEnabled(ShipperMaster.class, listbox_shipperDetailsList);
}
}
so what i think if i am calling setSortingEnabled() method from other class so i kept is as public and other method's i kept as private but it's giving me error as :
java.lang.NoSuchMethodError: com/itrucking/util/ZKUtil.getMapForSorting(Ljava/lang/Class;Lorg/zkoss/zul/Listbox;)Ljava/util/Map;
Why there is error NoSuchMethodError for ZKUtil.getMapForSorting() call in setSortingEnabled()
I know we can call private method from public in the same class. So i am not able to understand what is the problem.
Thanks in advance.
A NoSuchMethodError (the runtime error saying a method can't be found, instead of a compiler error) usually means that the .class files you're using are of a different version than the files you compiled against. In this case, you probably made changes to ZKUtil.java, but the JVM is loading an outdated version of ZKUtil.class. Clean and rebuild all of your .class files.