I have written a Thread Pool and I am not able to write the Junits(PowerMock) for that class.
public enum ThreadPool {
INSTANCE;
private static final String THREAD_POOL_SIZE = "threadpool.objectlevel.size";
private static TPropertyReader PROP_READER = new PropertyReader();
private final ExecutorService executorService;
private static final ILogger LOGGER = LoggerFactory
.getLogger(ReportExecutorObjectLevelThreadPool.class.getName());
ThreadPool() {
loadProperties();
int no_of_threads = getThreadPoolSize();
executorService = Executors.newFixedThreadPool(no_of_threads);
}
public void submitTask(Runnable task) {
executorService.execute(task);
}
private static void loadProperties() {
try {
PROP_READER.loadProperties("Dummy");
} catch (final OODSystemException e) {
LOGGER.severe("Loading properties for app failed!");
}
}
private int getThreadPoolSize() {
return Integer.valueOf(PROP_READER
.getProperty(THREAD_POOL_SIZE));
}
}
While Mocking this class I am getting NullPointerException in the line PROP_READER.loadProperties("DUMMY");
My Test Case is:-
PowerMockito.whenNew(PropertyReader.class).withNoArguments().thenReturn(mockPropertyReader);
PowerMockito.doNothing().when( mockPropertyReader,"loadProperties",anyString());
mockStatic(ThreadPool.class);
First you need to set your internal state of your enum as enum is final class
and the instance of an enum will be load on class loading
ThreadPool mockInstance = mock(ThreadPool .class);
Whitebox.setInternalState(ThreadPool.class, "INSTANCE", mockInstance);
then
PowerMockito.mockStatic(ThreadPool .class);
and then mocking
doNothing().when(mockInstance).loadProperties(any(String.class));
do not forget adding the following annotation to the test
#RunWith(PowerMockRunner.class)
#PrepareForTest({ThreadPool.class})
if it still not working you need to see which more member of the class you need to set in the internal state
Related
I need to do unit testing of methods of Singleton class which internally uses RxJava Singles, and used PowerMock test framework to mock static class and methods. I tried various method to mock Schedulers.io() and AndroidSchedulers.mainThread() methods but it's not working. I'm getting java.lang.NullPointerException error at line .subscribeOn(Schedulers.io()) inside UserApi.verifyUserData() method.
Singleton Class UserApi (Class under Test)
final public class UserApi {
private CompositeDisposable compositeDisposable;
private String userID;
//private final SchedulerProvider schedulerProvider;
private UserApi(String userId) {
super();
this.userID = userId;
//this.schedulerProvider = schedulerProvider;
}
public static UserApi getInstance() {
return SingletonHolder.sINSTANCE;
}
private static final class SingletonHolder {
private static final UserApi sINSTANCE;
static {
String uuid = UUID.randomUUID().toString();
sINSTANCE = new UserApi(uuid);
}
}
// Rest Api call
public void verifyUserData(byte[] doc, byte[] img) {
this.compositeDisposable = new CompositeDisposable();
String docStr = Base64.encodeToString(doc, Base64.NO_WRAP);
String imgStr = Base64.encodeToString(img, Base64.NO_WRAP);
final Disposable apiDisposable = IdvManager.getInstance().getUserManager().verifyUserData(docStr, imgStr)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<JsonObject>() {
#Override
public void accept(JsonObject verifyResponse) throws Exception {
pollResult();
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable error) throws Exception {
// handle error code...
}
});
this.compositeDisposable.add(apiDisposable);
}
private void pollResult() {
// code here...
}
}
UserManager Class and Interface
public interface UserManager {
Single<JsonObject> verifyUserData(String docStr, String imgStr);
}
final class UserManagerImpl implements UserManager {
private final UserService userService;
UserManagerImpl(final Retrofit retrofit) {
super();
this.userService = retrofit.create(UserService.class);
}
#Override
public Single<JsonObject> verifyUserData(String docStr, String imgStr) {
// Code here...
}
}
Unit Test
#RunWith(PowerMockRunner.class)
#PrepareForTest({IdvManager.class, Base64.class, Schedulers.class, AndroidSchedulers.class, UserApi.class})
public class UserApiTest {
#Mock
public UserManager userManager;
#Mock
private Handler handler;
private IdvManager idvManager;
private Schedulers schedulers;
private UserApi spyUserApi;
private TestScheduler testScheduler;
private String userID;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
handler = new Handler();
PowerMockito.suppress(constructor(IdvManager.class));
// mock static
PowerMockito.mockStatic(IdvManager.class);
PowerMockito.mockStatic(Schedulers.class);
PowerMockito.mockStatic(AndroidSchedulers.class);
PowerMockito.mockStatic(Base64.class);
// Create mock for class
idvManager = PowerMockito.mock(IdvManager.class);
schedulers = PowerMockito.mock(Schedulers.class);
PowerMockito.when(IdvManager.getInstance()).thenReturn(IdvManager);
when(idvManager.getUserManager()).thenReturn(userManager);
spyUserApi = PowerMockito.spy(UserApi.getInstance());
// TestSchedulerProvider testSchedulerProvider = new TestSchedulerProvider(testScheduler);
when(Base64.encodeToString((byte[]) any(), anyInt())).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return java.util.Base64.getEncoder().encodeToString((byte[]) invocation.getArguments()[0]);
}
});
when(schedulers.io()).thenReturn(testScheduler);
when(AndroidSchedulers.mainThread()).thenReturn(testScheduler);
userID = UUID.randomUUID().toString();
}
#After
public void clearMocks() {
//Mockito.framework().clearInlineMocks();
}
#Test
public void verifyUserData_callsPollResult_returnsResponse() {
// Input
String docStr = "iVBORw0KGgoAAAANSUhEUgAAAJ4AAACeCAYAAADDhbN7AA.....";
// Output
JsonObject verifyResponse = new JsonObject();
verifyResponse.addProperty("status", "Response created");
doReturn(Single.just(verifyResponse)).when(userManager).verifyUserData(docStr, docStr);
// spy method call
spyUserApi.verifyUserData(docFrontArr, docFrontArr);
testScheduler.triggerActions();
// assert
verify(userManager).verifyUserData(docStr, docStr);
}
}
Error
java.lang.NullPointerException
at com.rahul.manager.UserApi.verifyUserData(UserApi.java:60)
at com.rahul.manager.UserApiTest.verifyUserData_callsPollResult_returnsResponse(UserApiTest.java:171)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
I'm not sure whether i can test methods of Singleton class by spying on real instance of Singleton class using PowerMock.
Testing your code is complex because it's not testable and it's not extensible. It contains hardcoded dependencies everywhere (e.g. user id, handler, several singletons).
If you decide to use another id generation approach or another handler, you won't be able to do this without rewriting whole class.
Instead of hardcoding dependencies, ask for them in constructor (for mandatory dependencies) or setters (for optional ones).
This will make your code extensible and testable. After you do this, you will see your class contains several responsibilities, after moving them into separate classes, you will get much better picture :-)
For example:
public UserApi(String userId, Handler handle) {
this.userId = userId;
this.handler = handler;
}
Schedulers.io() is a static method, so you need to use mockStatic (which you did) and define the related mock accordingly.
I rearranged your setup method a bit, to improve the readability and fixed the mistake. You do not need an instance of Schedulers (The variable you named schedulers).
Probably a simple typo you made, as you did the right thing for Base64 and AndroidSchedulers.
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
testScheduler = new TestScheduler();
handler = new Handler();
// mock for instance of `IdvManager`
PowerMockito.suppress(constructor(IdvManager.class));
idvManager = PowerMockito.mock(IdvManager.class);
when(idvManager.getUserManager()).thenReturn(userManager);
// mock for `IdvManager` class
PowerMockito.mockStatic(IdvManager.class);
PowerMockito.when(IdvManager.getInstance()).thenReturn(idvManager);
// mock for `Schedulers` class
PowerMockito.mockStatic(Schedulers.class);
when(Schedulers.io()).thenReturn(testScheduler);
// spy for instance of `UserApi`
spyUserApi = PowerMockito.spy(UserApi.getInstance());
// mock for `Base64` class
PowerMockito.mockStatic(Base64.class);
when(Base64.encodeToString((byte[]) any(), anyInt())).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return java.util.Base64.getEncoder().encodeToString((byte[]) invocation.getArguments()[0]);
}
});
// mock for `AndroidSchedulers` class
PowerMockito.mockStatic(AndroidSchedulers.class);
when(AndroidSchedulers.mainThread()).thenReturn(testScheduler);
userID = UUID.randomUUID().toString();
}
However the NPE is missing the part that actually indicates its failing from this, consider adding it if that does not solve your problem.
I have a class.
public class FeedServiceImpl implements FeedService {
private final Map<FeedType, FeedStrategy> strategyByType;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public FeedServiceImpl(Map<FeedType, FeedStrategy> strategyByType) {
if (strategyByType.isEmpty()) throw new IllegalArgumentException("strategyByType map is empty");
this.strategyByType = strategyByType;
}
#Override
public void feed(LocalDate feedDate, FeedType feedType, String uuid) {
if (!strategyByType.containsKey(feedType)) {
throw new IllegalArgumentException("Not supported feedType: " + feedType);
}
executorService.submit(() -> runFeed(feedType, feedDate, uuid));
}
private FeedTaskResult runFeed(FeedType feedType, LocalDate feedDate, String uuid) {
return strategyByType.get(feedType).feed(feedDate, uuid);
}
}
How can I verify with Mockito that strategyByType.get(feedType).feed(feedDate, uuid) was called when I call feed method?
#RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> mock(FeedStrategy.class)));
}
#Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(strategyByType.get(TH_CREDIT), timeout(100)).feed(date, uuid);
}
}
This is my version. But I don't want to use Mockito timeout method. It's in my opinion not a good solution. Help me please!
When I test code that are dependent on executors I usually try to use an executor implementation that runs the task immediately in the same thread as submitted in order to remove all the hassle of multithreading. Perhaps you can add another constructor for your FeedServiceImpl class that lets you supply your own executor? The google guava lib has a MoreExecutors.directExecutor() method that will return such an executor. That would mean your test setup would change to something like:
service = new FeedServiceImpl(strategyByType, MoreExecutors.directExecutor());
The rest of the test code should then work as it is, and you can safely drop the timeout verification mode parameter.
A little improvement for #Per Huss answer so you won't have to change your constructor you can use ReflectionTestUtils class which is part of the spring-test package.
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
ReflectionTestUtils.setField(service , "executorService", MoreExecutors.newDirectExecutorService());
}
Now in the tests your executorService will implement the MoreExecutors.newDirectExecutorService() and not Executors.newSingleThreadExecutor().
If you want to run a real FeedStrategy (rather than a mocked one, as suggested in another answer) then you'll need a way for the FeedStrategy to somehow indicate to the test thread when it is finished. For example, by using a CountDownLatch or setting a flag which your test can wait on.
public class FeedStrategy {
private boolean finished;
public void feed(...) {
...
this.finished = true;
}
public boolean isFinished() {
return this.finished;
}
}
Or perhaps the FeedStrategy has some side effect which you could wait on?
If any of the above are true then you could use Awaitility to implement a waiter. For example:
#Test
public void aTest() {
// run your test
// wait for completion
await().atMost(100, MILLISECONDS).until(feedStrategyHasCompleted());
// assert
// ...
}
private Callable<Boolean> feedStrategyHasCompleted() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
// return true if your condition has been met
return ...;
}
};
}
You need to make a mock for FeedStrategy.class visible to your test, so that you will be able to verify if the FeedStrategy.feed method was invoked:
#RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
private FeedStrategy feedStrategyMock;
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
feedStrategyMock = Mockito.mock();
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> feedStrategyMock));
}
#Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(feedStrategyMock).feed(date, uuid);
}
}
I am coding Dropwizard micro-services that fetch data in a MongoDB database. The micro-services work fine but I'm struggling to use in my DAO the configuration coming from my Dropwizard configuration Java class. Currently I have
public class XDAO implements IXDAO {
protected DB db;
protected DBCollection collection;
/* singleton */
private static XDAO instance;
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO();
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase();
initDatabaseIndexes();
}
private void initDatabase(){
MongoClient client = null;
try {
client = new Mongo("10.126.80.192",27017);
db = client.getDB("terre");
//then some other code
}
catch (final MongoException e){
...
}
catch (UnknownHostException e){
...
}
}
}
I want to unhard-code the three arguments in these two lines :
client = new Mongo("10.126.80.192", 27017);
db = client.getDB("terre");
My MongoConfiguration Java class is :
public class MongoConfiguration extends Configuration {
#JsonProperty
#NotEmpty
public String host;
#JsonProperty
public int port = 27017;
#JsonProperty
#NotEmpty
public String db_name;
public String getMongohost() {
return host;
}
public void setMongohost(String host) {
this.host = host;
}
public int getMongoport() {
return port;
}
public void setMongoport(int port) {
this.port = port;
}
public String getDb_name() {
return db_name;
}
public void setDb_name(String db_name) {
this.db_name = db_name;
}
}
My Resource class that uses the DAO is :
#Path("/mongo")
#Produces(MediaType.APPLICATION_JSON)
public class MyResource {
private XDAO xDAO = XDAO.getSingleton();
private String mongohost;
private String db_name;
private int mongoport;
public MyResource(String db_name, String mongohost, int mongoport) {
this.db_name = db_name;
this.mongohost = mongohost;
this.mongoport = mongoport;
}
public MyResource() {
}
#GET
#Path("/findByUUID")
#Produces(value = MediaType.APPLICATION_JSON)
#Timed
public Entity findByUUID(#QueryParam("uuid") String uuid) {
return xDAO.findByUUid(uuid);
}
}
And in my application class there is
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport());
environment.jersey().register(resource);
}
To solve my problem I tried many things. The last thing I tried was to add these four fields in my XDAO
private String mongohost;
private String db_name;
private int mongoport;
private static final MongoConfiguration configuration = new MongoConfiguration();
Coming with this piece of code in the constructor of the XDAO:
public XDAO(){
instance.mongohost = configuration.getMongohost();
instance.mongoport = configuration.getMongoport();
instance.db_name = configuration.getDb_name();
/* then like before */
initDatabase();
initDatabaseIndexes();
}
When I try this I have a null pointer exception when my initDatabase method is invoked : mongoHost and db_name are null
The problem is that you are creating a new configuration in your XDAO with private static final MongoConfiguration configuration = new MongoConfiguration(); instead of using the config from Dropwizard's run method.
When you do this, the fields host and db_name in the new configuration are null, which is why you are getting the NPE when instantiating XDAO
You need to pass the instance of MongoConfiguration that you get from Dropwizard in your application class to your XDAO, ideally when the singleton XDAO is created so it has non-null values for db_name and host
This code below part of the problem - you are creating the singleton without giving XDAO the MongoConfiguration configuration instance.
public class XDAO implements IXDAO {
//... snip
/* Get singleton */
public static synchronized XDAO getSingleton(){
if (instance == null){
instance = new XDAO(); // no configuration information is included!
}
return instance;
}
/* constructor */
public XDAO(){
initDatabase(); // this call needs db_name & host but you haven't set those yet!!
initDatabaseIndexes();
}
I recommend you modify your application class to create XDAO along the lines of this:
#Override
public void run(final MongoConfiguration configuration, final Environment environment) {
XDAO XDAOsingleton = new XDAO(configuration);
XDAO.setSingletonInstance(XDAOsingleton); // You need to create this static method.
final MyResource resource = new MyResource(configuration.getDb_name(), configuration.getMongohost(), configuration.getMongoport()); // MyResource depends on XDAO so must be created after XAO's singleton is set
environment.jersey().register(resource);
}
You may also need to take initDatabase() etc out of XDAO's constructor depending on if you keep public static synchronized XDAO getSingleton()
I also recommend you change the constructor of MyResource to public MyResource(XDAO xdao). The resource class doesn't appear to need the configuration information, and it is better to make the dependency on an XDAO explicit (you then also don't need to keep the XDAO singleton in a static field inside XDAO's class).
To get MongoDB integrated in a simple way to Dropwizard, please try and use MongoDB Managed Object. I will explain this in 3 simple steps:
Step 1: Create a simple MongoManged class:
import com.mongodb.Mongo;
import io.dropwizard.lifecycle.Managed;
public class MongoManaged implements Managed {
private Mongo mongo;
public MongoManaged(Mongo mongo) {
this.mongo = mongo;
}
#Override
public void start() throws Exception {
}
#Override
public void stop() throws Exception {
mongo.close();
}
}
Step 2: Mention MongoDB Host, Port, DB Name in a config yml file:
mongoHost : localhost
mongoPort : 27017
mongoDB : softwaredevelopercentral
Step 3: Bind everything together in the Application Class:
public class DropwizardMongoDBApplication extends Application<DropwizardMongoDBConfiguration> {
private static final Logger logger = LoggerFactory.getLogger(DropwizardMongoDBApplication.class);
public static void main(String[] args) throws Exception {
new DropwizardMongoDBApplication().run("server", args[0]);
}
#Override
public void initialize(Bootstrap<DropwizardMongoDBConfiguration> b) {
}
#Override
public void run(DropwizardMongoDBConfiguration config, Environment env)
throws Exception {
MongoClient mongoClient = new MongoClient(config.getMongoHost(), config.getMongoPort());
MongoManaged mongoManaged = new MongoManaged(mongoClient);
env.lifecycle().manage(mongoManaged);
MongoDatabase db = mongoClient.getDatabase(config.getMongoDB());
MongoCollection<Document> collection = db.getCollection(config.getCollectionName());
logger.info("Registering RESTful API resources");
env.jersey().register(new PingResource());
env.jersey().register(new EmployeeResource(collection, new MongoService()));
env.healthChecks().register("DropwizardMongoDBHealthCheck",
new DropwizardMongoDBHealthCheckResource(mongoClient));
}
}
I have used these steps and written a blog post and a sample working application code is available on GitHub. Please check: http://softwaredevelopercentral.blogspot.com/2017/09/dropwizard-mongodb-tutorial.html
i'm trying to test my bean as a component itself. So that the method i want to test gets executed correctly. Therefore i'm mocking it's dependencies with JMockit. I wrote two tests, one for validating the if condition to true, so the method ends immediately and returns null.The second one for executing the code below this condition also resulting in returning null. But my code coverage tool (JaCoCo) just shows that the if condition is executed and not the code below.
Session is a field in the super class ÀbstractBean. The method isLoggedIn() invokes session.isLoggedIn() and is defined in AbstractBean.
TrendBean
#Named(value = "trendBean")
#ViewScoped
#SuppressWarnings("deprecation")
public class TrendBean extends AbstractBean implements Serializable {
private static final long serialVersionUID = -310401000218411384L;
private static final Logger logger = Logger.getLogger(TrendBean.class);
private ChartPoint point;
private List<ChartPoint> points;
#Inject
private ITrendManager manager;
public String addChartPoint() {
if (!isLoggedIn()) {
return null; //only this block is executed
}
assertNotNull(point);
final User user = getSession().getUser();
manager.addPointToUser(user, point);
FacesContext.getCurrentInstance().addMessage(
null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
getTranslation("pointAdded"), ""));
init();
return null;
}
}
TrendBeanTest
public class TrendBeanTest {
#Tested
TrendBean trendBean;
#Injectable
LoginBean loginBean;
#Injectable
Session session;
#Injectable
ITrendManager manager;
#Injectable
IUserManager userManager;
#Test
public void testAddChartPoint() {
new NonStrictExpectations() {
{
session.isLoggedIn();
result = true;
session.getUser();
result = (User) any;
manager.addPointToUser((User) any, (ChartPoint) any);
};
};
Deencapsulation.setField(trendBean, "point", new ChartPoint());
assertEquals(null, trendBean.addChartPoint());
}
#Test
public void testAddChartPointNotLoggedIn() {
new Expectations() {
{
manager.addPointToUser((User) any, (ChartPoint) any);
times = 0;
};
};
Session s = new Session();
s.setUser(null);
Deencapsulation.setField(trendBean, "session", s);
assertEquals(null, trendBean.addChartPoint());
}
}
AbstractBean
public abstract class AbstractBean {
private static final Logger logger = Logger.getLogger(AbstractBean.class);
#Inject
private Session session;
public boolean isLoggedIn() {
return session.isLoggedIn();
}
}
For anyone having familiar problems the statement
session.isLoggedIn();
result = true;
in Expectations Block was the answer. Although i'm facing new problems, i'm going to ask a new question.
I have Java app with Spring running on tomcat.
This class is causing a very strange problem for me:
#WebListener
public class ThreadPool extends ThreadPoolExecutor implements ServletContextListener {
private ThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_TIMEPERIOD, TimeUnit.SECONDS, taskQueue);
}
private static final ThreadPoolExecutor pool = new ThreadPool();
public synchronized static void submit(Task task) {
executingTasks.add(task);
pool.execute(task);
}
#Override
public synchronized void contextDestroyed(ServletContextEvent arg0) {
cancelWaitingTasks();
sendStopSignalsToExecutingTasks();
pool.shutdown();
}
...
}
If the constructor is private or default I get this exception during runtime (on first HTTP request to the app):
Error configuring application listener of class com.testApp.util.ThreadPool
java.lang.IllegalAccessException: Class org.apache.catalina.core.DefaultInstanceManager can not access a member of class com.testApp.util.ThreadPool with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:140)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Skipped installing application listeners due to previous error(s)
Error listenerStart
Context [] startup failed due to previous errors
But if i set the constructor public then I get no exceptions and everything works fine. Can anyone tell me why is this default or private constructor causing runtime exceptions?
Tomcat uses Class.newInstance() to create an instance of your ThreadPool. This method obeys the access rules of Java.
Since your constructor is private it fails with a IllegalAccessException. This is the runtime equivalent of not being allowed to call a function to the compiler error which you see if you would try to write new ThreadPool() outside of ThreadPool,
Tomcat's org.apache.catalina.core.DefaultInstanceManager is trying to create an object of your ThreadPool which you have configured as context listener. Now, since this is outside of org.apache.catalina.core you have to use a public constructor else org.apache.catalina.core.DefaultInstanceManager will not be able to create its object.
From org.apache.catalina.core.DefaultInstanceManager
private Object newInstance(Object instance, Class<?> clazz) throws IllegalAccessException, InvocationTargetException, NamingException {
if (!ignoreAnnotations) {
Map<String, String> injections = injectionMap.get(clazz.getName());
processAnnotations(instance, injections);
postConstruct(instance, clazz);
}
return instance;
}
Through the error, it said clearly because it cannot access a member of class.
can not access a member of class com.testApp.util.ThreadPool with modifiers
I think I accidentally discovered the real reason I was getting exceptions. Currently I am using this class, no exceptions thrown, tested on GlassFish and Tomcat:
public class TrackingThreadPool extends ThreadPoolExecutor {
private static final int MAX_WAITING_TASKS = 4000;
private static final int MAX_ACTIVE_THREADS = 20;
private static final int MIN_ACTIVE_THREADS = 4;
private static final int DEACTIVATE_THREADS_AFTER_SECONDS = 60;
private TrackingThreadPool() {
super(MIN_ACTIVE_THREADS, MAX_ACTIVE_THREADS, DEACTIVATE_THREADS_AFTER_SECONDS,
TimeUnit.SECONDS, waitingTasks);
}
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final TrackingThreadPool instance = new TrackingThreadPool();
public synchronized static void submitAndTrack(Task task) {
executingTasks.put(task.getId(), task);
instance.execute(task);
}
public synchronized static void shutdownAndCancelAllTasks() {
cancelWaitingTasks();
sendStopSignalToExecutingTasks();
instance.shutdown();
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (r instanceof Task) {
executingTasks.remove(((Task) r).getId());
}
}
private static void cancelWaitingTasks() {
List<Runnable> waitingTaskListRunnables = new ArrayList<>(waitingTasks.size() + 10); //+10 to avoid resizing
waitingTasks.drainTo(waitingTaskListRunnables);
for (Runnable r : waitingTaskListRunnables) {
if (r instanceof Task) {
((Task) r).sendStopSignal(byShutdownMethod());
}
}
}
private static void sendStopSignalToExecutingTasks() {
for (long taskId : executingTasks.keySet()) {
Task executingTask = executingTasks.get(taskId);
executingTask.sendStopSignal(byShutdownMethod());
}
}
private static String byShutdownMethod() {
return TrackingThreadPool.class.getSimpleName() + "#shutdownAndCancelAllTasks()";
}
}
And if I swap the positions of BlockingQueue<Runnable> waitingTasks and TrackingThreadPool instance like this:
private static final TrackingThreadPool instance = new TrackingThreadPool();
private static final Map<Long, Task> executingTasks = new HashMap<>(MAX_ACTIVE_THREADS * 2);
private static final BlockingQueue<Runnable> waitingTasks = new LinkedBlockingQueue<>(MAX_WAITING_TASKS);
I get exceptions again because waitingTasks is not instantiated by the time I make a new TrackingThreadPool instance.
I guess you can have a subclass of ThreadPoolExecutor with a private constructor / singelton pattern.