I have been writing a program. Everything is in program is controlled by the 'Engine' class. I have hence made it a singleton. Here is my current code that runs just fine.
package org.bautista.cybersafe.core;
import javax.swing.SwingUtilities;
import org.bautista.cybersafe.ui.MainUI;
import org.bautista.cybersafe.util.Cache;
import org.bautista.cybersafe.util.Config;
import org.bautista.cybersafe.util.account.Account;
import org.bautista.cybersafe.util.account.AccountManager;
import org.bautista.cybersafe.util.user.User;
import org.bautista.cybersafe.util.user.UserManager;
public class Engine {
private static Engine instance;
private AccountManager accountManager;
private final MainUI ui;
private final UserManager userManager;
private final Config config;
private User currentUser;
private Engine() {
instance = this; //THIS IS LINE 22
if (!Cache.cacheExists()) {
if (!Cache.createCache()) {
System.out.println("Error creating cache.");
}
}
config = new Config();
userManager = new UserManager();
ui = new MainUI();
}
public static Engine getInstance() {
return instance == null ? instance = new Engine() : instance;
}
public void setCurrentUser(User user) {
currentUser = user;
}
public User getCurrentUser() {
return currentUser;
}
public AccountManager getAccountManager() {
return accountManager;
}
public Config getConfig() {
return config;
}
public UserManager getUserManager() {
return userManager;
}
public void logOut() {
currentUser = null;
accountManager = null;
ui.showLogin();
}
public void openAccountViewer(final Account account) {
ui.showAccount(account);
ui.setTitle("Cyber Safe - [" + currentUser.getUsername() + "] -"
+ account.getName());
}
public void openCreateAccountScreen() {
ui.showCreateAccount();
}
public void openCreateUserScreen() {
ui.showCreateUser();
}
public void openLoginScreen() {
ui.showLogin();
ui.setTitle("Cyber Safe");
}
public void openSafeScreen() {
if (accountManager == null) {
accountManager = new AccountManager(currentUser);
}
ui.showSafe();
ui.setTitle("Cyber Safe - [" + currentUser.getUsername() + "]");
}
public void refreshUI() {
ui.refresh();
}
public void updateAccountPreviews() {
ui.updateAccountScroller();
}
public void run() {
try {
SwingUtilities.invokeAndWait(() -> ui.setVisible(true));
} catch (final Exception e) {
e.printStackTrace();
}
}
}
When I comment out line 22
instance = this;
I receive a StackOverflowerror. When I debug the program I find that the Engine constructor is being called repeatedly, as if it were performing recursion, until I final get the error. Why does that happen? Should't my #getInstance() method be initiating instance as a new instance of the 'Engine' class?
Here is the stacktrace:
Exception in thread "main" java.lang.StackOverflowError
at java.io.InputStream.<init>(InputStream.java:45)
at java.io.FileInputStream.<init>(FileInputStream.java:123)
at org.bautista.cybersafe.util.Config.loadProperties(Config.java:67)
at org.bautista.cybersafe.util.Config.<init>(Config.java:29)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:28)
at org.bautista.cybersafe.core.Engine.getInstance(Engine.java:34)
at org.bautista.cybersafe.util.user.UserManager.loadUsers(UserManager.java:73)
at org.bautista.cybersafe.util.user.UserManager.<init>(UserManager.java:20)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:29)
at org.bautista.cybersafe.core.Engine.getInstance(Engine.java:34)
at org.bautista.cybersafe.util.user.UserManager.loadUsers(UserManager.java:73)
at org.bautista.cybersafe.util.user.UserManager.<init>(UserManager.java:20)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:29)
at org.bautista.cybersafe.core.Engine.getInstance(Engine.java:34)
at org.bautista.cybersafe.util.user.UserManager.loadUsers(UserManager.java:73)
at org.bautista.cybersafe.util.user.UserManager.<init>(UserManager.java:20)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:29)
at org.bautista.cybersafe.core.Engine.getInstance(Engine.java:34)
at org.bautista.cybersafe.util.user.UserManager.loadUsers(UserManager.java:73)
at org.bautista.cybersafe.util.user.UserManager.<init>(UserManager.java:20)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:29)
And here is the full project on Github
Thanks in advance!
The stack trace shows the following loop:
at org.bautista.cybersafe.util.user.UserManager.loadUsers(UserManager.java:73)
at org.bautista.cybersafe.util.user.UserManager.<init>(UserManager.java:20)
at org.bautista.cybersafe.core.Engine.<init>(Engine.java:29)
at org.bautista.cybersafe.core.Engine.getInstance(Engine.java:34)
Engine.getInstance() calls new Engine().
new Engine() calls new UserManager().
new UserManager() calls UserManager.loadUsers().
UserManager.loadUsers() calls Engine.getInstance(), but Engine.instance hasn't been assigned yet, since the previous new Engine() call hasn't returned yet.
This is why assigning Engine.instance in the constructor, before it calls new UserManager(), fixes the problem.
You should reorganize your code to prevent that initialization loop. UserManager and Engine should not be co-dependent during initialization.
Note that doing private static Engine instance = new Engine() as suggested in another answer will not fix your initialization loop.
Related
I am implementing a command pattern in android.
This is what I have right now. For some reason this does not run. It is like the AddUserRequest is getting garbage collected for some reason.
RequestManager.java:
public class RequestManager extends BroadcastReceiver {
private static final RequestManager instance = new RequestManager();
private boolean isConnected = false;
private static ArrayList<Request> requestQueue = new ArrayList<Request>();
private RequestManager() {
}
/* singleton class */
public static RequestManager getInstance() {
return instance;
}
public void invokeRequest(Request request) {
request.execute(); // only to test this, this will change
return;
}
}
AddUserRequest.java
public class AddUserRequest extends InsertionRequest {
User user;
public AddUserRequest(User user) {
this.user = user;
}
public void execute() {
System.out.println("TEST!!!");
}
}
Request.java:
public abstract class Request {
public abstract void execute();
}
}
InsertionRequest.java
public abstract class InsertionRequest extends Request {
}
RequestManagerTest.java
public class RequestManagerTest extends ActivityInstrumentationTestCase2 {
public RequestManagerTest(){
super(MainActivity.class);
}
public void testAddUserRequest() {
User user = new User();
user.setName("Tester12345");
AddUserRequest request = new AddUserRequest(user);
RequestManager.getInstance().invokeRequest(request);
}
}
For some reason this does not print "TEST!!!" and for the life of me I cannot figure out why. I looked in the debug log and everytime request.execute() in RequestManager.java gets called there is a "GC Explicit..." which I suspect has to do with garbage collection. What is the proper way to do what I am trying to do?
I believe I've seen variants of this question, but no "definitive answer". In the code below, I understand that SomeEventManager holds a reference to someImplClassTwo.myEventListenerA and someImplClassTwo.myEventListenerB, and that this does not allow for someImplClassTwo to be garbage collected, and this results in the output generated the second time someEventManager.notifyListeners() is invoked.
But, I'd really like for users of SomeImplClass not to have to know that there are listeners involved in the implementation, and that these listeners need to be manually un-registered (i.e., SomeImplClass.releaseListeners()) before releasing the SomeImplClass object.
Is there a clean/accepted way of doing this?
p.s. I've already played with finalize(), just for fun, and confirmed that GC is not even attempted in this case, for either instance of SomeImplClass. So, that seems to be a non-starter as a potential solution.
Test Driver
public class TestDriver {
public static void main(String[] args) {
SomeEventManager someEventManager = SomeEventManager.getInstance();
SomeImplClass someImplClassOne = new SomeImplClass("One");
SomeImplClass someImplClassTwo = new SomeImplClass("Two");
someEventManager.notifyListeners();
someImplClassOne.releaseListeners();
someImplClassOne = null;
someImplClassTwo = null;
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
}
someEventManager.notifyListeners();
}
}
Event Interface
public interface SomeEventListener {
public void handleSomeEvent();
}
Event Manager
import java.util.ArrayList;
import java.util.List;
public class SomeEventManager {
private static SomeEventManager eventManager = null;
private List<SomeEventListener> listeners = null;
private SomeEventManager() {
listeners = new ArrayList<SomeEventListener>();
}
public static SomeEventManager getInstance() {
if (eventManager == null) {
eventManager = new SomeEventManager();
}
return eventManager;
}
public void addListener(SomeEventListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeListener(SomeEventListener listener) {
listeners.remove(listener);
}
public void notifyListeners() {
for(SomeEventListener listener : listeners) {
listener.handleSomeEvent();
}
}
}
Event Listener Implementation
public class SomeImplClass {
private InnerEventListener myEventListenerA = null;
private InnerEventListener myEventListenerB = null;
private String id = null;
public SomeImplClass(String id) {
this.id = id;
myEventListenerA = new InnerEventListener(id + "_A");
myEventListenerB = new InnerEventListener(id + "_B");
}
public void releaseListeners() {
myEventListenerA.unregisterListener();
myEventListenerB.unregisterListener();
}
private class InnerEventListener implements SomeEventListener {
private SomeEventManager someEventManager = null;
private String id = null;
public InnerEventListener(String id) {
someEventManager = SomeEventManager.getInstance();
this.id = id;
registerListener();
}
public void registerListener() {
someEventManager.addListener(this);
}
public void unregisterListener() {
someEventManager.removeListener(this);
}
public void handleSomeEvent() {
System.out.println("InnerEventListener->" + id);
}
}
}
The solution we use is to have the listener automatically unregister itself if it gets called and the thing it's updating has been collected.
It looks a bit like this:
private static class InnerEventListener implements SomeEventListener {
private final WeakReference<ThingToUpdate> thingRef;
public InnerEventListener(ThingToUpdate thing) {
thingRef = new WeakReference<>(thing);
}
#Override
public void handleSomeEvent(SomeEvent event) {
ThingToUpdate thing = thingRef.get();
if (thing != null) {
thing.updateSomehow();
} else {
((SomeEventedThing) event.getSource())
.removeSomeEventListener(this);
}
}
}
//...
SomeEventedThing eventedThing;
ThingToUpdate thingToUpdate;
//...
eventedThing.addListener(new InnerEventListener(thingToUpdate));
I wouldn't say it's a perfect solution because the listener sticks around until it gets an event, and it's still somewhat dependent on garbage collection. We've been trying to replace it with explicit removal where possible, usually on addNotify/removeNotify on GUI components.
I'm dwelling with an autoWired service class which is null in a Spring Boot application.. Every object is instantiated by Spring, so I don't know why it happens.
The situation is:
I have a Rele.java class which is the following:
#Component
public class Rele {
private Pin pin;
private GpioController gpio;
private GpioPinDigitalOutput relePin;
private static final Logger logger = Logger.getLogger(Rele.class);
private Interruttore interruttore;
#Autowired AccensioneService accensioneService;
public Rele(){
}
// Costruttore
public Rele(Pin pin, Interruttore interruttore) {
this.pin = pin;
this.gpio = GpioFactory.getInstance();
this.relePin = gpio.provisionDigitalOutputPin(pin, "MyRele", PinState.LOW);
this.interruttore = interruttore;
}
public void lightOn() {
try {
if (relePin.isLow()) {
relePin.high();
updateAccensione(interruttore, true);
logger.debug("Rele acceso");
}
} catch (NullPointerException e) {
logger.debug("relepin è:" +relePin);
logger.debug("gpio è:"+gpio);
}
}
public void lightOff() {
if (relePin.isHigh()) {
relePin.low();
updateAccensione(interruttore, false);
logger.debug("Rele spento");
}
}
public void updateAccensione(Interruttore interruttore, boolean acceso) {
Date lastDateAccensione = new Date();
try {
logger.debug("accensioneService is"+accensioneService);
lastDateAccensione = accensioneService.findLastDate(interruttore);
} catch(NullPointerException npe){
logger.debug("accensioneService is: "+accensioneService);
logger.error("Error is:", npe);
lastDateAccensione = new Timestamp(lastDateAccensione.getTime());
}
Accensione accensione = new Accensione();
Date date = new Date();
logger.debug("lastDate:" + lastDateAccensione);
accensione.setDateTime(new Timestamp(date.getTime()));
accensione.setInterruttore(interruttore);
accensione.setIsLit(acceso);
accensione.setLastDateTime(lastDateAccensione);
logger.debug("Accensione è:"+accensione.toString());
accensioneService.saveAccensione(accensione);
}
public Pin getPin() {
return pin;
}
public void setPin(Pin pin) {
this.pin = pin;
}
public Interruttore getInterruttore() {
return interruttore;
}
public void setInterruttore(Interruttore interruttore) {
this.interruttore = interruttore;
}
public GpioPinDigitalOutput getRelePin() {
return relePin;
}
public void setRelePin(GpioPinDigitalOutput relePin) {
this.relePin = relePin;
}
public GpioController getGpio() {
return gpio;
}
public void setGpio(GpioController gpio) {
this.gpio = gpio;
}
}
When trying to call for updateAccensione, this is null.
Rele is created from a Controller, by this method
#RequestMapping(value="/illuminazione")
public ResponseEntity<Illuminazione> findIlluminazione(#RequestParam(value="idLuce") int idLuce,
#RequestParam(value="lit") boolean lit,
#RequestParam(value="suServer") boolean suServer) {
Illuminazione illuminazione = new Illuminazione();
Date lastDate = illuminazioneService.findLastDate(idLuce);
illuminazione.setLastDateTime(lastDate);
illuminazione.setIdLuce(idLuce);
illuminazione.setIsLit(lit);
Date date = new Date();
illuminazione.setDateTime(new Timestamp(date.getTime()));
illuminazioneService.saveIlluminazione(illuminazione);
logger.debug("Aggiornata luce " + idLuce + " accesa: "+lit);
//managing rele
if(suServer){
//check if status has changed
Luce luce = luceService.findById(idLuce);
int idInterruttore = luce.getInterruttore().getIdInterruttore();
Interruttore interruttore = interruttoreService.findById(idInterruttore);
Rele rele = releService.findByInterruttore(interruttore);
logger.debug("rele="+rele.toString());
if(lit){
rele.lightOn();
} else {
rele.lightOff();
}
}
return new ResponseEntity<Illuminazione>(illuminazione,HttpStatus.OK);
}
Rele is created, i find it in my logs.
AccensioneService is an interface, it's concrete implementation is AccensioneServiceImpl:
#Service("accensioneService")
#Transactional
public class AccensioneServiceImpl implements AccensioneService{
#Autowired AccensioneDao dao;
#Override
public void saveAccensione(Accensione accensione) {
dao.saveAccensione(accensione);
}
#Override
public Accensione findById(int id) {
return dao.findById(id);
}
#Override
public Date findLastDate(Interruttore interruttore) {
return dao.findLastDate(interruttore);
}
#Override
public boolean findLastStatus(int id) {
return dao.findLastStatus(id);
}
#Override
public void updateAccensione(Interruttore interruttore) {
}
}
I don't know if anything else is needed. AccensioneService is also called in other methods and controller, and it works... only when called inside Rele gives me this error...
Edited to add
You must be calling new Rele() or the other Rele(Pin, Interruttore ) constructor? If you are calling these in your code, the accensioneService will be null because Spring needs to create the bean, you cannot create it with its constructor if you want beans Autowired into it or for it to be Autowired. If you want it to behave like this, Spring has to know about it, so it has to be in (and come from) the Spring context.
Put a log statement in each constructor and find out who is calling them, and fix that so that instead of calling the constructor, you get the bean from Spring.
Old answer below
You need to post this method to be sure:
Rele rele = releService.findByInterruttore(interruttore);
I'll bet you are creating rele somewhere by calling new Rele(), which is not correct. You need to let Spring create it for you.
You did not post enough code to give further suggestions.
Also, you say this is null. What this are you talking about?
I have this console application, but for some reason the thread's run() method doesn't want to start. The code seems long for the first time but I tried to organize it as much as I can.
The result output:
eThread starting!!
So it seems that CarManager.startFunctionalities() gets executed, but the line eThread.start() is not executed at all because the line "started" is not printed out.
Here is the sourcecode.
The main class:
package rpicar.android;
public class AndroidEmulator{
public static void main(String args[]) throws InterruptedException {
CarManager cm = new CarManager ("localhost");
}
}
CarManager:
package rpicar.android;
import rpicar.common.Direction;
import rpicar.common.EnvironmentData;
public class CarManager {
private MotorManager mManager;
private final String RPIADDRESS = "localhost";
private Thread mThread; //motor
private EnvironmentManager eManager;
private Thread eThread;
public CarManager(String rpiAddress) {
//initialize MotorManager
mManager = new MotorManager(RPIADDRESS);
//Make a thread for the Motor commands
mThread = new Thread(mManager);
//Initialize EnvironmentManager
eManager = new EnvironmentManager(RPIADDRESS);
//Makea thread for collecting EnvironmentData
eThread = new Thread (eThread);
startFunctionalities();
}
public void move(Direction d){
this.mManager.setDirection(d);
}
public EnvironmentData getCurrentEnvironmentData(){
return this.eManager.getCurrentEnvironmentData();
}
private void startFunctionalities(){
//Start MotorManager for sending movement commands when needed.
//mThread.start();
//Start EnvironmentManager to collect EnvironmentData
System.out.println("eThread starting!! ");
eThread.start();
}
}
EnvironmentManager:
package rpicar.android;
import rpicar.common.CarComponent;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import rpicar.common.EnvironmentData;
public class EnvironmentManager extends CarComponent implements Runnable{
private EnvironmentData currentEnvironmentData;
public EnvironmentManager(String rpiAddress) {
super(rpiAddress, 2176, true);
this.currentEnvironmentData = new EnvironmentData();
}
public synchronized EnvironmentData getCurrentEnvironmentData() {
return currentEnvironmentData;
}
public synchronized void setCurrentEnvironmentData(EnvironmentData currentEnvironmentData) {
this.currentEnvironmentData = currentEnvironmentData;
}
#Override
public void run() {
System.out.println("eThread started!! ");
super.connect();
while(true){
try {
this.setCurrentEnvironmentData((EnvironmentData) super.in.readObject());
} catch (IOException ex) {
super.connect();
} catch (ClassNotFoundException ex) {
Logger.getLogger(EnvironmentManager.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
When you create your instance of eThread, you accidentally pass the thread itself to the constructor (or according to the order of the operations, you pass null).
You should pass eManager to the constructor instead.
eThread = new Thread (eThread);
Would become
eThread = new Thread (eManager);
You can protect yourself in the future from this mistake by making the eThread a final field, so you cannot use it before you declare it.
I have been struggling to find a good way of implementing PubSub with Autobahn for android. I am currenty using the Singleton pattern to use the same AutobahnConnection in my whole app. I got the calls and subscribing working but when i unsubscribe and then come back to the same fragment and try to subscribe again it doesnt work. Below my current Autobahn Class:
package nl.w3s.hulpverlener.utils;
import nl.w3s.hulpverlener.helper.DebugHelper;
import android.util.Log;
import de.tavendo.autobahn.Autobahn;
import de.tavendo.autobahn.Autobahn.SessionHandler;
import de.tavendo.autobahn.AutobahnConnection;
import de.tavendo.autobahn.AutobahnOptions;
public final class AutobahnService{
private static AutobahnService INSTANCE;
private static AutobahnConnection connection;
private AutobahnOptions options;
private boolean connected = false;
private String url = "http://johelpen.w3s.nl/";
private String websocketUrl;
private AutobahnService() {
connection = new AutobahnConnection();
options = new AutobahnOptions();
options.setReceiveTextMessagesRaw(true);
websocketUrl = CommonUtilities.STAGING_WEBSOCKET_URL;
connect();
}
public static AutobahnService getInstance() {
if(INSTANCE == null)
INSTANCE = new AutobahnService();
else
INSTANCE.connect();
return INSTANCE;
}
public void connect() {
if(!connection.isConnected()) {
connection.connect(websocketUrl, new SessionHandler() {
#Override
public void onOpen() {
connected = true;
Log.i(DebugHelper.TAG_DEBUG, "CONNECTED");
}
#Override
public void onClose(int p_intCode, String p_strReason) {
connected = false;
Log.i(DebugHelper.TAG_DEBUG, "DISCONNECTED");
}
}, options);
}
}
public void doCall(final String callType, final Class<?> classRef, final Autobahn.CallHandler autobahnEventHandler, final Object... arguments) {
connection.call(url + "#" + callType, classRef, autobahnEventHandler, arguments);
}
public void doSubscribe(final String callType, final Class<?> classRef, final Autobahn.EventHandler autobahnEventHandler) {
connection.subscribe(url + callType, classRef, autobahnEventHandler);
}
public void doUnsubscribe(final String callType) {
connection.unsubscribe(url + callType);
}
}
When I look at my logs it doesnt disconnect while unsubscribing and resubscribing.