Observablevalue check for new and old values equally - javafx

In fact I have a class that performs the job of collecting website post URL.The url article I get when analyzing each page site
+http://dantri.com.vn/xa-hoi.htm
+http://dantri.com.vn/xa-hoi/trang-2.htm
....
+http://dantri.com.vn/xa-hoi/trang-9998.htm
+http://dantri.com.vn/xa-hoi/trang-n.htm
to the Property url is as follows.
public class Pagination {
private final StringProperty postURL = new SimpleStringProperty();
public String getPostURL() {
return postURL.get();
}
public void setPostURL(String value) {
postURL.set(value);
}
public StringProperty postURLProperty() {
return postURL;
}
public void gather() {
for (int i = 0; i < n; i++) {
for (String url : getAllURLToPage("http://dantri.com.vn/xa-hoi/trang-"+i+".htm")) {
setPostURL(url);
}
}
}
}
From page http://dantri.com.vn/xa-hoi/trang-9998.htm===->http://dantri.com.vn/xa-hoi/trang-n.htm.
The post url value is constant, so I want to terminate the program when consecutive url are equal,eg newValue.equals(oldValue) as below
public static void main(String[] args) {
Pagination pagination = new Pagination();
pagination.postURLProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.equals(oldValue)) {
System.out.println("BREAK");
}
}
});
pagination.gather();
}
That's just the example by newValue and oldValue are always not equal with observableValue.
I hope you help me solve my problem how can i control post url i get

As I noted in the comment, a ChangeListener is invoked in response to a change in a property. If you call postURL.set(...) and pass the same value that is currently held in postURL, the change listener will not be invoked. (TBH it's not really clear what the purpose of the property is here.)
You could just do this in the for loop directly:
public void gather() {
for (int i = 0; i < n; i++) {
for (String url : getAllURLToPage("http://dantri.com.vn/xa-hoi/trang-"+i+".htm")) {
if (Objects.equals(url, getPostURL())) {
System.out.println("BREAK");
}
setPostURL(url);
}
}
}
or perhaps in the set method, assuming you don't call postURL.set(...) anywhere else:
public void setPostURL(String value) {
if (Objects.equals(value, postURL.get())) {
System.out.println("BREAK");
}
postURL.set(value);
}
But there is no way for a ChangeListener to detect a lack of a change.

Related

RestController JSON Response object format

I am using Spring Boot to return data from a Repository. I would like to format my JSON so that it plays nicely with ExtJS' ajax handling. Essentially I would like to include properties to handle success/failure, count, and errorMsg along with a List of data from the repository.
I have tried by creating a ResponseDTO object that I'm returning from my Rest Controller.
#RestController
public class AdminController {
private static final Logger logger = LogManager.getLogger(AdminController.class);
#Autowired
private UserService userService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#GetMapping("/searchUsers")
public ResponseDTO searchUsers(String name, String active) {
int activeFlag;
List<User> users;
ResponseDTO resp;
if(active.equals("true")) {
activeFlag = 1;
} else activeFlag=0;
if(StringUtils.isEmpty(name)) {
users= userService.findAllUsers(activeFlag);
} else {
users= userService.findByUsernameActive(name, activeFlag);
}
return new ResponseDTO(users, true);
}
}
Here's my DTO that I use in the controller:
public class ResponseDTO {
private boolean success;
private int count = 0;
private List<?> values;
public boolean getSuccess() {
return this.success;
}
public void setState(boolean st) {
this.success=st;
}
public int getCount() {
return this.count;
}
public void setCount(int cnt) {
this.count=cnt;
}
public List<?>getValues() {
return this.values;
}
public void setValues(List<?> vals) {
this.values = vals;
}
public ResponseDTO(List<?> items, boolean state) {
this.success = state;
values = items;
this.count = items.size();
}
}
Here's what the JSON I get back looks like:
{
"ResponseDTO": {
"success":true,
"count":2,
"values":[{obj1 } , { obj2}]
}
}
what I would like to get is something more like:
{
"success" : true,
"count" : 2,
"values" [{obj1},{obj2}]
}
I'm using Spring Boot and Jackson annotations. I have used an annotation to ignore individual fields in the objects in the results array, but I can't find a way to unwrap the ResponseDTO object to not include the class name.
When you serialize ResponseDTO POJO, you should not get 'ResponseDTO' in the response by default. Because, the root wrap feature is disabled by default. See the doc here. If you have the below code, please remove it.
mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);

JavaFX – ObservableList and list's item change

This answer provides a solution for an observable list that will send "list updated" notifications if properties of elements of the list change.
In my case, elements (a Element class) of such observable list are complex and I don't like to implement property for each member variable. Due to this, I added into the Element class a BooleanProperty that indicates change of the class.
Element Class
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class Element {
// ...
private ReadOnlyBooleanWrapper changeIndicatorWrapper;
public Element() {
//...
changeIndicatorWrapper = new ReadOnlyBooleanWrapper(false);
}
public ReadOnlyBooleanProperty changeIndicatorProperty() {
return changeIndicatorWrapper.getReadOnlyProperty();
}
public void someMethod() {
// Some update
changeIndicatorWrapper.set(!changeIndicatorWrapper.get());
}
}
Observable List
ObservableList<Element> elementsObservableList = FXCollections.observableList(
new ArrayList<>(),
(Element element) -> new Observable[] { element.changeIndicatorProperty() }
);
elementsObservableList.addListener(new ListChangeListener<Element>() {
#Override
public void onChanged(Change<? extends Element> c) {
System.out.println("CHANGE");
while(c.next()) {
if (c.wasUpdated()) {
for (int i = c.getFrom(); i < c.getTo(); ++i)
System.out.println(elementsObservableList.get(i));
}
}
}
});
My question is about this approach. Repeatedly set the changeIndicatorProperty to true not fire the change event. So, I need to reverse changeIndicatorProperty value changeIndicatorWrapper.set(!changeIndicatorWrapper.get()) each time. It is strange, isn't it?
Can I force programatically the update event?
It is strange, isn't it?
No this isn't surprising. For a change to be triggered a change needs to happen. If the BooleanProperty determines no change does happen and therefore the listeners are not notified of anything, this still satisfies the contract of Property.
Actually a Property isn't needed anyways. What is needed is a Observable that notifies it's observers. You could do this by using the following class and calling invalidate:
public class SimpleObservable implements Observable {
private final List<InvalidationListener> listeners = new LinkedList<>();
#Override
public void addListener(InvalidationListener listener) {
listeners.add(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
listeners.remove(listener);
}
public void invalidate() {
for (InvalidationListener listener : listeners) {
try {
listener.invalidated(this);
} catch (RuntimeException ex) {
}
}
}
}
Example:
public class Element {
protected final SimpleObservable observable = new SimpleObservable();
public Observable getObservable() {
return observable;
}
public static <T extends Element> ObservableList<T> observableArrayList() {
return FXCollections.observableArrayList(e -> new Observable[]{e.observable});
}
private void update() {
observable.invalidate();
}
public static void main(String[] args) {
ObservableList<Element> list = Element.observableArrayList();
list.addListener((ListChangeListener.Change<? extends Element> c) -> {
while (c.next()) {
if (c.wasUpdated()) {
System.out.println("update: [" + c.getFrom() + ", " + c.getTo() + ")");
}
}
});
list.addAll(new Element(), new Element(), new Element());
list.get(1).update();
}
}

JavaFX - Incompatible parameter type with using TreeView.EditEvent in lambda

In a JavaFX TreeView I'm using 'custom' classes which extend TreeItem. This makes me able to edit the items in the TreeView (I can double click them and edit the contents when running the application) but I can't seem to be able to set the .setOnEditCommit() method properly. I was hoping it'd work similar as the function in a tableview but I didn't have any luck yet.
This is my code in my controller in which I try to set the setOnEditCommit() method. In my TreeView called 'trvDivisies' I display football team divisions / competitions and one level lower I display all the teams that are in a certain division.
private void setUpTreeView() {
trvDivisies.setEditable(true);
trvDivisies.setShowRoot(false);
TreeItem<String> root = new TreeItem<>();
for (Divisie d : divisies) {
TreeItem<String> divisieTreeItem = d;
divisieTreeItem.valueProperty().set(d.getNaam());
for (VoetbalTeam vt : d.getVoetbalTeams()) {
TreeItem<String> voetbalTeamTreeItem = vt;
voetbalTeamTreeItem.valueProperty().setValue(vt.getTeamNaam());
divisieTreeItem.getChildren().add(voetbalTeamTreeItem);
}
root.getChildren().add(divisieTreeItem);
}
trvDivisies.setRoot(root);
trvDivisies.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println(newValue);
}
});
trvDivisies.setCellFactory(TextFieldTreeCell.forTreeView());
// I get an error at the following line when compiling
trvDivisies.setOnEditCommit((TreeView.EditEvent p) -> {
TreeItem<String> selectedItem = p.getTreeItem();
if (selectedItem instanceof Divisie) {
updateDivisie((Divisie)selectedItem);
} else if (selectedItem instanceof VoetbalTeam) {
updateTeam((VoetbalTeam)selectedItem);
}
});
}
This is what my 'custom' classes look like.
public class Divisie extends TreeItem<String> {
private static int idCount = 0;
private int id;
private String naam;
private List<VoetbalTeam> voetbalTeams;
public int getId() {
return id;
}
public String getNaam() {
return naam;
}
public List<VoetbalTeam> getVoetbalTeams() {
return voetbalTeams;
}
public Divisie(int id, String naam) {
super(naam);
this.id = id;
this.naam = naam;
}
public Divisie(String naam) {
this.id = ++idCount;
this.naam = naam;
}
public void addTeam(VoetbalTeam toBeAdded) {
if (voetbalTeams == null) {
voetbalTeams = new LinkedList<>();
}
voetbalTeams.add(toBeAdded);
}
#Override
public String toString() {
return this.naam;
}
}
Second 'lower level' class
public class VoetbalTeam extends TreeItem<String> {
private static int idCount = 0;
private int id;
private String teamNaam;
private List<Speler> spelers;
public int getId() {
return id;
}
public String getTeamNaam() {
return teamNaam;
}
public List<Speler> getSpelers() {
return this.spelers;
}
public VoetbalTeam(int id, String teamNaam) {
super(teamNaam);
this.id = id;
this.teamNaam = teamNaam;
}
public VoetbalTeam(String teamNaam) {
super(teamNaam);
this.id = ++idCount;
this.teamNaam = teamNaam;
}
public void addSpeler(Speler nieuweSpeler) {
if (spelers == null) {
spelers = new LinkedList<>();
}
this.spelers.add(nieuweSpeler);
}
#Override
public String toString() {
return this.teamNaam;
}
}
When trying to run the application WITH the .setOnEditCommit() method I get an error saying:
Error:(97, 37) java: incompatible types: incompatible parameter types in lambda expression
I was hoping you guys can tell me what I need to change my TreeView.EditEvent lambda to or help me find an easier solution.
For a TreeView<T>, the signature of setOnEditCommit is
void setOnEditCommit(EventHandler<TreeView.EditEvent<T>> value)
Since you have (apparently) a TreeView<String>, you need
trvDivisies.setOnEditCommit((TreeView.EditEvent<String> p) -> {
// ...
});
Or, of course, you can just let the compiler do the work for you:
trvDivisies.setOnEditCommit(p -> {
// ...
});

JSF custom panel with button - action not invoked

I have built a custom component button, but somehow the action is not invoked. When debugging the getAction-Method within the component and invoking the supplied MethodeExpression the Bean-Method is called as expected. But due to some reason, the Expression is not invoked when pressing the button in the browser.
Is there some kind of additional Interface necessary to pass the action to the embedded button-component?
Any help is very appreciated since I am stuck at this issue for some days now
MyClass:
public class MyClass extends UIPanel implements SystemEventListener
{
private UIForm form;
private HtmlCommandButton buttonOk;
public MyClass()
{
FacesContext context = getFacesContext();
UIViewRoot root = context.getViewRoot();
root.subscribeToViewEvent(PostAddToViewEvent.class, this);
}
#Override
public void processEvent(SystemEvent event)
{
this.form = new UIForm();
this.buttonOk = new HtmlCommandButton();
this.buttonOk.setId("okButtonId");
this.buttonOk.setActionExpression(getAction());
this.buttonOk.setValue("OK");
this.form.getChildren().add(this.buttonOk);
getChildren().add(this.form);
}
private enum PropertyKeys
{
action, text, titel
}
public MethodExpression getAction()
{
return (MethodExpression) getStateHelper().eval(PropertyKeys.action);
}
public void setAction(MethodExpression actionExpression)
{
getStateHelper().put(PropertyKeys.action, actionExpression);
}
public String getText()
{
return (String) getStateHelper().eval(PropertyKeys.text);
}
public void setText(String text)
{
getStateHelper().put(PropertyKeys.text, text);
}
public String getTitel()
{
return (String) getStateHelper().eval(PropertyKeys.titel);
}
public void setTitel(String titel)
{
getStateHelper().put(PropertyKeys.titel, titel);
}
#Override
public void encodeAll(FacesContext context) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement(HTML.DIV_ELEM, this);
writer.writeText(getText(), null);
this.form.encodeAll(context);
writer.endElement(HTML.DIV_ELEM);
}
#Override
public void encodeChildren(FacesContext context) throws IOException
{
}
#Override
public boolean isListenerForSource(Object source)
{
return (source instanceof MyClass);
}
}
MyClassHandler:
public class MyClassHandler extends ComponentHandler
{
public MyClassHandler(ComponentConfig config)
{
super(config);
}
#SuppressWarnings("rawtypes")
#Override
protected MetaRuleset createMetaRuleset(Class type)
{
return super.createMetaRuleset(type).addRule(new MethodRule("action", String.class, new Class[] { ActionEvent.class }));
}
}
myView Method:
...
public String myMethod()
{
System.err.println("myMethod");
return "/some/path/yadayada.xhtml";
}
...
MyView.xhtml
<myTag action="#{myView.myMethod}" id="id1" titel="bla" text="bleh" />
Exdending UICommand is enough, since you only want one action to be executed.
You have to provide two additional MethodExpressions via the tag-attributes and within the decode-method you can check which button has been pressed and redirect the particular MethodExpression to the standard-action provided by UICommand. This way, you dont have to worry about the legacy-interface ActionSource, or how Events are broadcasted.
public void decode(FacesContext contex)
{
Map<String,String> map = context.getExternalContext.getRequestParameterMap();
// your rendered buttons need a name you check for
final boolean okPressed = map.containsKey( getClientId + ":ok" );
final boolean cancelPressed = map.containsKey( getClientId + ":cancel" );
if(okPressed || cancelPressed)
{
MethodExpression exp = null;
if(okPressed)
{
exp = getActionOk();
}
else
{
exp = getActionCancel();
}
// redirect to standard action
setActionExpression(exp);
queueEvent(new ActionEvent(this));
}
}
In order to make use of of this you need two attributes (actionOk and actionCancel) which use Method Expressions (setter and getter). Those have to be configured by a ComponentHandler as you did for the action-attribute.

Capturing HTML generated from ASP.NET

How do I best capture the HTML (in my instance, for logging) rendered by an aspx-page?
I dont want to have to write back to the page using Response.Write, since it messes up my site layout.
Using the Response.OutputStream or Response.Output's stream results in an ArgumentException ({System.ArgumentException: Stream was not readable.)
Good question, i had to try out and see if i could create a HttpModule to do what you are describing.
I didnt have any luck trying to read from the responsestream, but using the ResponseFilter gave me a way to capture the content.
The following code seems to work pretty good, and i figured maybe you could use the code as a base. But remember this is just something i threw together fast, it has not been tested in any way. So dont use it in any production environment without proper reviewing/testing and such. Feel free to comment on it though ;)
public class ResponseLoggerModule : IHttpModule
{
private class ResponseCaptureStream : Stream
{
private readonly Stream _streamToCapture;
private readonly Encoding _responseEncoding;
private string _streamContent;
public string StreamContent
{
get { return _streamContent; }
private set
{
_streamContent = value;
}
}
public ResponseCaptureStream(Stream streamToCapture, Encoding responseEncoding)
{
_responseEncoding = responseEncoding;
_streamToCapture = streamToCapture;
}
public override bool CanRead
{
get { return _streamToCapture.CanRead; }
}
public override bool CanSeek
{
get { return _streamToCapture.CanSeek; }
}
public override bool CanWrite
{
get { return _streamToCapture.CanWrite; }
}
public override void Flush()
{
_streamToCapture.Flush();
}
public override long Length
{
get { return _streamToCapture.Length; }
}
public override long Position
{
get
{
return _streamToCapture.Position;
}
set
{
_streamToCapture.Position = value;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
return _streamToCapture.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _streamToCapture.Seek(offset, origin);
}
public override void SetLength(long value)
{
_streamToCapture.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_streamContent += _responseEncoding.GetString(buffer);
_streamToCapture.Write(buffer, offset, count);
}
public override void Close()
{
_streamToCapture.Close();
base.Close();
}
}
#region IHttpModule Members
private HttpApplication _context;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
_context = context;
context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
context.PreSendRequestContent += new EventHandler(context_PreSendRequestContent);
}
void context_PreRequestHandlerExecute(object sender, EventArgs e)
{
_context.Response.Filter = new ResponseCaptureStream(_context.Response.Filter, _context.Response.ContentEncoding);
}
void context_PreSendRequestContent(object sender, EventArgs e)
{
ResponseCaptureStream filter = _context.Response.Filter as ResponseCaptureStream;
if (filter != null)
{
string responseText = filter.StreamContent;
// Logging logic here
}
}
#endregion
}
Many load testers will allow you to log the HTTP responses generated, but bear in mind with ASP.NET those could be some very large log-files.
Edit: Response.Filter as per Tom Jelen's code is designed to give this kind of oversight and Response.Outputstream is otherwise unreadable.
Edit 2: For a page rather than a HTTPModule
public class ObserverStream : Stream
{
private byte[] buffer = null;
private Stream observed = null;
public ObserverStream (Stream s)
{
this.observed = s;
}
/* important method to extend #1 : capturing the data */
public override void Write(byte[] buffer, int offset, int count)
{
this.observed.Write(buffer, offset, count);
this.buffer = buffer; //captured!
}
/* important method to extend #2 : doing something with the data */
public override void Close()
{
//this.buffer available for logging here!
this.observed.Close();
}
/* override all the other Stream methods/props with this.observed.method() */
//...
}
and in your Page_Load (or before your response is written anyway)
Response.Filter = new ObserverStream(Response.Filter);
One way to to make server-side XMLHTTP request to your own server. Grab the result and save it to a file or DB.
Alternately you can use AJAX on the client, grab the result, and POST it back to the server.

Resources