Say I have a Handler that is logging data to some object via a listener.
public Object execute(ExecutionEvent event) throws ExecutionException {
IHandlerService service;
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
try {
RecordingDocument d = new RecordingDocument("TestProject", "Tester", true);
d.record();
MessageDialog.openInformation(
window.getShell(),
"JavaTV",
"You are now recording.");
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
This object is created when one of the menu items is selected and begins logging to a data structure inside the object.
How do I retrieve this document from a different handler? I need this if someone uses the menu to stop the recording.
It seems like a general Java question on how to make some object accessible by other objects. Obviously, you need to store it in a place, where it can be accessed by others (provide a getter, put to the registry, store to the DB, serialize to the hard drive, etc). There are many design patterns to solve your problem, so no ideal answer can be provided.
You can't, probably use getters, because as you mentioned Handler is created at the time menu is executed. I think that handler is not recreated each time, but only on first access, so you could make an instance variable in the handler, but this doesn't seem right.
Storing to a DB and serialization is probably way too overkill at this stage for you, so I would suggest you to put object to the registry, using Registry pattern (think of it as a global cache). So, here is what you could do:
public class DocumentsRegistry {
private Map<String, RecordingDocument> registry = new HashMap<String, RecordingDocument>();
private static DocumentRegistry instace = new DocumentRegistry();
public static DocumentRegistry getInstance() {
return instance;
}
public void registerDocument(String key, RecordingDocument document) {
registry.put(key, document);
}
public RecordingDocument getDocument(String key) {
return registry.get(key);
}
}
// your handler
public static final String DOCUMENT_KEY = "DOCUMENT";
public Object execute(ExecutionEvent event) throws ExecutionException {
IHandlerService service;
IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
try {
RecordingDocument d = new RecordingDocument("TestProject", "Tester", true);
d.record();
MessageDialog.openInformation(
window.getShell(),
"JavaTV",
"You are now recording.");
DocumentsRegistry.getInstance().registerDocument(DOCUMENT_KEY, d);
} catch (CoreException e) {
e.printStackTrace();
}
return null;
}
// another handler
public Object execute(ExecutionEvent event) throws ExecutionException {
RecordingDocument d = DocumentsRegistry.getInstance().getDocument(DOCUMENT_KEY);
// do something with it
return null;
}
If you want to support concurrent recordings, so that many documents can be opened at the same time, you would need a solution for generating keys per document.
Related
How do I write in same text file from different classes in java.
One of the class call method from another class.
I do not want to open BufferedWriter in each class, so thinking if there is a cleaner way to do this ?
So essentially, I want to avoid writing the following code in each class
Path path = Paths.get("c:/output.txt");
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write("Hello World !!");
}
A good way of doing this is to create a central writing class, that maps from a file name to a reader/writer-object. For example:
public class FileHandler {
private static final Map<String, FileHandler> m_handlers = new HashMap<>();
private final String m_path;
private final BufferedWriter m_writer;
// private final BufferedReader m_reader; this one is optional, and I did not instantiate in this example.
public FileHandler (String path) {
m_path = path;
try {
m_writer = Files.newBufferedWriter(path);
} catch (Exception e) {
m_writer = null;
// some exception handling here...
}
}
public void write(String toWrite) {
if (m_writer != null) {
try {
m_writer.write(toWrite);
} catch (IOException e) {
// some more exception handling...
}
}
}
public static synchronized void write(String path, String toWrite) {
FileHandler handler = m_handlers.get(path);
if (handler == null) {
handler = new FileHandler(path);
m_handlers.put(path, toWrite);
}
handler.write(toWrite);
}
}
Be aware that this behavior does not close the file writers at any point, because you don't know who else is currently (or later on) writing. This is not a complete solution, just a strong hint in a good direction.
This is cool, because now you can "always" call FileHandler.write("c:output.txt", "Hello something!?$");. The FileHandler class could be extended (as hinted) to read files too, and to do other stuff for you, that you might need later (like buffer the content, so you don't have to read a file every time you access it).
I have a library which parse URLs and extract some data. There is one class per URL. To know which class should handle the URL provided by the user, I have the code below.
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
The problem is users are requesting more URL to be parsed, which means my switch statement is growing. Every time someone comes up with a parser, I have to modify my code to include it.
To end this, I've decided to create a map and expose it to them, so that when their class is written, they can register themselves to the factory, by providing the host name, and the extractor to the factory. Below is the factory with this idea implemented.
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
And the user would use it that way:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
I was patting my own back when I realized this will never work: the user classes are not loaded when I receive the URL, only the factory, which means their constructor never runs, and the map is always empty. So I am back to the drawing board, but would like some ideas around getting this to work or another approach to get rid of this pesky switch statement.
S
Another option is to use the Service Loader approach.
Having your implementers add something like the following in ./resources/META-INF/services/your.package.HostExtractor:
their.package1.HostExtractorABC
their.package2.HostExtractorDEF
their.package3.HostExtractorGHI
...
Then in your code, you can have something like:
HostExtractorFactory() {
final ServiceLoader<HostExtractor> loader
= ServiceLoader.load(your.package.HostExtractor.class);
for (final HostExtractor registeredExtractor : loader) {
// TODO - Perform pre-processing which is required.
// Add to Map? Extract some information and store? Etc.
}
}
I would advice for you to learn about dependency injection (I love spring implementation). You will then be able to write an interface like
public interface HostExtractorHandler {
public String getName();
public HostExtractor getInstance();
}
Than your code can "ask" for all classes that implements this interface, you then would be able to build your map in the initialization phase of your class.
I would use the Reflections library to locate the parsers. They all appear to derive from the HostExtractor class, so use the library to locate all subtypes:
Reflections reflections = new Reflections("base.package");
Set<Class<? extends HostExtractor>> extractorTypes =
reflections.getSubTypesOf(HostExtractor.class);
Use the results to create the instances in your factory:
for (Class<? extends HostExtractor> c : extractorTypes) {
HostExtractor he = c.newInstance();
EXTRACTOR_MAPPING.put(he.getHostName(), he);
}
I made up the getHostName method, but it should be trivial to add to the HostExtractor base class.
I have this Thread inside my Project which runs continously accepting new symbols
public class StreamThread extends Thread {
private Set<String> allSymbolSet = new HashSet<String>(Arrays.asList("USBC", "TCSD", "PCLJ"));
private PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();
public void addSymbols(String str) {
if (str != null) {
priorityBlocking.add(str);
}
}
public void run() {
while (true) {
try {
while (priorityBlocking.peek() != null) {
String symbol = priorityBlocking.poll();
allSymbolSet.add(symbol);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
my question is , i want to access the variable allSymbolSet from another class
what will be the best approach to get access to this variable named allSymbolSet from anoter class , for this i have two choices
modify the access specifier of allSymbolSet from private to default .
Write a get Method which is supoused to return the Set
Please suggest me , what will be the good approach in this case ??
Best approach would be the getter method AND synchronize the access to the object allSymbolSet, something like this:
public Set<String> getAllSymbolSet() {
synchronized(allSymbolSet) {
return allSymbolSet;
}
}
and also synchronize the access to allSymbolSet inside your thread.
A few comments:
If you make the set non-private, some code could modify it (by mistake or on purpose) which could result in inconsistent behaviour in your StreamThread class. Don't do that.
Providing a simple getter does not solve the issue above. Prefer returning a copy of your set.
Make your variables final whenever you can when in a multi-threading environment - it solves many thread safety issues.
Prefer implementing Runnable than extending Thread
You will need to synchronize ALL accesses to your set (read and write), for example by using a synchronizedSet or even better by wrapping a ConcurrentHashMap which generally provides better performance.
instead of peek+poll you can simply take from your queue
So your final class could look like:
public class StreamTask implements Runnable {
private final Set<String> allSymbolSet;
private final PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();
public StreamTask() {
String[] symbols = {"USBC", "TCSD", "PCLJ"};
//use a thread safe set, for example based on ConcurrentHashMap
allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
Collections.addAll(allSymbolSet, symbols);
}
public void addSymbols(String str) {
if (str != null) {
priorityBlocking.add(str);
}
}
public Set<String> getSymbols() {
return new HashSet<> (allSymbolSet); //return a copy
}
public void run() {
while (true) {
try {
allSymbolSet.add(priorityBlocking.take());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Finally, I might be missing something, but that class looks equivalent to the much simpler:
public class StreamTask {
private final Set<String> allSymbolSet;
public StreamTask() {
String[] symbols = {"USBC", "TCSD", "PCLJ"};
//use a thread safe set, for example based on ConcurrentHashMap
allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
Collections.addAll(allSymbolSet, symbols);
}
public void addSymbols(String str) {
if (str != null) {
allSymbolSet.add(str);
}
}
public Set<String> getSymbols() {
return new HashSet<> (allSymbolSet); //return a copy
}
}
Better way is method 2. Writing a getter method. If you want to allow set the values then use a setter later. Then your data will be encapsulated .
Write a get Method which is supposed to return the Set. by using this your private remains private and you also access it from outside using Object of the same class.
I seem not to grasp the concept of Events and such. After reading a while on how to implement the listeners and such I came across the Java tutorial saying I should extend AbstractListModel to get the data event firing. For some reason it still doesn't work.
Is there anything I'm doing wrong?
And what kind of code is expected at addListDataListener(ListDataListener l) for it to work? Since I don't understand that either.
public class CarComboBox extends AbstractListModel<Object> implements ComboBoxModel<Object> {
private JdbcRowSet jdbc;
private int size = 0;
private String selection;
public CarComboBox() {
try {
jdbc = new Query().getCarInfo();
jdbc.beforeFirst();
while (jdbc.next()) {
size++;
}
jdbc.beforeFirst();
}
catch (SQLException ex) {
System.err.println(ex.toString());
}
}
#Override
public void setSelectedItem(Object anItem) {
selection = (String) anItem;
}
#Override
public Object getSelectedItem() {
return selection;
}
#Override
public void addListDataListener(ListDataListener l) {
}
#Override
public void removeListDataListener(ListDataListener l) {
}
#Override
public int getSize() {
return size;
}
#Override
public String getElementAt(int index) {
try {
jdbc.absolute(index + 1);
return jdbc.getString(2);
}
catch (SQLException ex) {
System.out.println(ex.toString());
}
return null;
}
}
And to add a listener to the CarComboBox I do:
CarComboBox ccb = new CarComboBox();
ccb.addListDataListener(new ListDataListener()
I'm guessing that you are using the official tutorial.
However you should not touch ListModel and ComboBoxModel. Those are more advanced features you probably do not need.
The 4 examples in the tutorial do NOT use ListModel and ComboBoxModel.
If you use a standard JComboBox (no ListModel or ComboBoxModel), what happens is that when someone makes a selection, an ActionEvent is fired. This event is magically fired by Swing; you don't have to worry about how it is generated. However what is your responsibility is to have some (zero, one or more) objects being able to receive and do something about the ActionEvent:
public class MyClass implements ActionListener {
JComboBox comboBox = ...;
...
// You must register explicitly every ActionListener that you
// want to receive ActionEvent's from comboBox.
// Here we register this instance of MyClass.
comboBox.addActionListener(this);
...
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JComboBox) {
System.out.println("MyClass registered an ActionEvent from a JComboBox.");
System.out.println("Selected: " +
((JComboBox) e.getSource()).getSelectedItem());
}
}
}
Note that if you don't have any other ActionEvent's fired by different Swing components you
can skip the if (e.getSource() instanceof JComboBox) since you know your ActionEvent always comes from a JComboBox.
In my example the JComboBox is inside MyClass, but it does not have to be:
JComboBox comboBox = ...;
MyClass myClass = ...;
comboBox.addActionListener(myClass);
...
comboBox.addActionListener(someOtherActionListener);
You don't need to override addListDataListener() and removeListDataListener() method. The AbstractListModel already take care of the listeners. Here is the implementation of AbstractListModel.addListDataListener():
public void addListDataListener(ListDataListener l) {
listenerList.add(ListDataListener.class, l);
}
The idea of abstract classes is that they do most of the work for you. Usually you only need to implement abstract methods.
XXListener and XXModel are different sides of the coin: the former is the observer to the latter which is the observable. The listener registers itself to the model when it wants to get notified on changes. It's the responsibility of the model to
manage its listeners (that's typically handled already by the AbstractXXModel, as already explained by #userWhateverNumber ;)
fire the notifications if appropirate: that's the part a custom model must take over, in your case
like
#Override
public void setSelectedItem(Object item) {
selection = item;
fireContentChanged(this, -1, -1);
}
Arguably (there are personal preferences around :-) you often don't need custom model implementations but can just as well re-use the provided DefaultXXModels. In your context and assuming the content of the resultset is immutable it might be an option to fill the default model with the data at construction time, like
DefaultComboBoxModel model = new DefaultComboBoxModel();
forEachRowInResultSet {
model.addElement(resultSet.getString(2));
}
If, on the other hand, the content changes then your model implementation is invalid anyway: the model must notify its listeners whenever something had changed
Object one = model.getElementAt(index);
Object other = model.getElementAt(index)
if (!one.equals(other)) {
listener must have received a contentsChanged
}
The dialogue and the array displays just fine, I just want to be able to set the static variable from the originating class within the onClick that is in a method that is in a different class. All of the try, catch and
<?> were things that I put in at the insistence of the compiler:
public class Setter
{
public void myList(Context context, Class<?> thisclass, int arrayid, String choice)
{
return new AlertDialog.Builder(context)
.setItems(arrayid, new OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
setChoice(thisclass, context, arrayid, which, choice);
}
})
.create();
}
public void setChoice(Class<?> thisclass, Context context, int arrayid, int which, String choice)
{
String[] array = context.getResources().getStringArray(arrayid);
try
{
Field f = thisclass.getDeclaredField(choice);
f.set(null, array[which]);
}
catch (SecurityException e)
{
e.printStackTrace();
}
catch (NoSuchFieldException e)
{
e.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}
}
public class ClassA extends Activity
{
static String stringa;
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
AlertDialog thinga = setted.myList(this, getclass(), R.array.thinga, stringa).show();
...
}
}
When I select an item from the list, I get this from debugger:
ClassCache.findFieldByName(Field[], String) line: 438
Class.getDeclaredField(String) line: 666
Setter.setChoice(Class, Context, int, int, String) line: 45 // the line with the Field
I think I'm passing it the class wrong but this is a bit out of my current depth.
I have a number of different classes each with their own static Strings. I am passing the method below the name of the String (in choice) and the context of what I had hoped was the original class that called a method that called a method that led to the code below. I was hoping I could call context.choice = something and the machine would read that as ClassA.stringa = something; how do I do that?
Briefly, I want to have a list of items that the user can choose from be the content of a dialogue, and have their selection be saved and accessible to the class that called for the creation of the dialogue. Perhaps I'm going about this all wrong but I got tired of dealing with other 'kludges' involving using spinners to do the same thing.
Because onClick can't have non-final objects declared elsewhere (at least that is my understanding) I thought maybe I could get around that by calling to another method, setChoice that would store the value of whatever was chosen. I would definitively say this is a kludge and would love to be shown the light as to how you are supposed to deal with these things.
Java does not have closures, but you can get close with anonymous inner classes.
String output;
public void onCreate() {
Setter.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
output = "selected";
}
});
}
See also this swing tutorial: http://download.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
Edit:
In spirit of your example, this should look like this:
public class Setter
{
public void setChoice(IsetString setter, String something)
{
setter.setString(something);
}
}
class ClassA extends Activity implements setString
{
static String stringa;
string polka = "dots";
Setter setted = new Setter();
...
public void onCreate()
{
super.onCreate();
...
setted.setChoice(new IsetString() {
#Override
public void setString(String s) {
stringa = s;
}
}, polka);
...
}
}
interface IsetString {
void setString(String s);
}
The short answer - use the Reflection API.
The long answer - you'll need to obtain access to the Fields of the desired Context Class. Once you gain access to the Field instances, you can set their values using the set() method; the API call is a bit tricky in that you'll need to pass in the object reference (the context object and not the context class) whose field you wish to modify.
It is necessary that your Context, choice and something parameters to the method, contain the necessary information to make this operation as simple as possible. In other words, the Context class might have to contain the actual Class object (or provides a way to get one) that contains the field.
You can use reflection for that. Suppose you context is class itself
public void setChoice(Class<?> context, String choice, String something)
{
try {
Field f = context.getDeclaredField(choice);
f.set(null, something);
} catch (....) {
}
}
Add proper exception handling
Note that first argument to set is null. That is only valid for static methods. So you may want to check that method is static using f.getModifiers().