How to use optionalBlock in build step's config.jelly - java

I have problem with creating constructor, which Jenkins can call for some JSON data originating from a Jelly form,. For testing, I created a minimal Jenkins plugin with mvn hpi:create and following two custom files:
src/main/resources/foo/hyde/jenkins/plugins/OptionalBlockSampleBuilder/config.jelly
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:block>
<table>
<f:optionalBlock name="enableText" title="Enable optional text" checked="${instance.enableText}">
<f:entry title="Optional text" field="text">
<f:textbox />
</f:entry>
</f:optionalBlock>
</table>
</f:block>
src/main/java/foo/hyde/jenkins/plugins/OptionalBlockSampleBuilder.java
package foo.hyde.jenkins.plugins;
public class OptionalBlockSampleBuilder extends hudson.tasks.Builder {
public final String text;
public final boolean enableText;
#org.kohsuke.stapler.DataBoundConstructor
public OptionalBlockSampleBuilder(String text, Boolean enableText) {
this.text = text;
this.enableText = (enableText != null) && enableText;
}
#Override
public boolean perform(hudson.model.AbstractBuild build, hudson.Launcher launcher, hudson.model.BuildListener listener) {
listener.getLogger().println("OptionalBlockSampleBuilder " + enableText + "/" + text);
return true;
}
#hudson.Extension
public static final class DescriptorImpl extends hudson.tasks.BuildStepDescriptor<hudson.tasks.Builder> {
public boolean isApplicable(Class<? extends hudson.model.AbstractProject> aClass) {
return true;
}
public String getDisplayName() {
return "Optional Block Sample";
}
}
}
I'm building against pom.xml parent <groupId>org.jenkins-ci.plugins</groupId><artifactId>plugin</artifactId><version>1.454</version>, and everything builds, Netbeans 6.9.1 launches Debug Jenkins and I get to create a job with this build step. Everything works if I don't check that checkbox, and I get expected OptionalBlockSampleBuilder false/null to job's console output.
But if I do check the checkbox and add text, then saving/applying the job config gives this exception from the depths of Jenkins code, when it tries to call my constructor:
java.lang.RuntimeException:
Failed to instantiate class
foo.hyde.jenkins.plugins.OptionalBlockSampleBuilder
from {
"enableText":{"text":"xx"},
"kind":"foo.hyde.jenkins.plugins.OptionalBlockSampleBuilder",
"stapler-class":"foo.hyde.jenkins.plugins.OptionalBlockSampleBuilder"
}
There has to be a simple fix. I have tried many different changes, and also tried to see how other plugins use it, and finally created this minimal test plugin. How to fix it to make optionalBlock work?

The hint comes from the JSON data:
{
"enableText":{"text":"xx"},
"kind":"foo.hyde.jenkins.plugins.OptionalBlockSampleBuilder",
"stapler-class":"foo.hyde.jenkins.plugins.OptionalBlockSampleBuilder"
}
You can see here that enableText contains a child property, text. That means that the f:optionalBlock is actually expecting an encapsulation of all the fields contained within the block -- when the block is checked, you will receive an instance of the encapsulation field class; when it is unchecked, that field will be null. To use the optionalBlock properly, you would need the #DataBoundConstructor to take in a single nullable class instance that encapsulates the entire optionalBlock. For example:
private String text;
#DataBoundConstructor
public MyClass(EnableTextBlock enableText)
{
if (enableText != null)
{
this.text = enableText.text;
}
}
public static class EnableTextBlock
{
private String text;
#DataBoundConstructor
public EnableTextBlock(String text)
{
this.text = text;
}
}
Notice that the enableText field in this case is actually an instance of EnableTextBlock class, which contains a child property, text. That will satisfy the JSON object that is being sent in the form.
Instead, if all you need is a single field that has a checkbox to enable entry of that field, you might want to consider instead using the f:optionalProperty tag, which will take care of that single-field encapsulation for you. However, in many cases, the optionalBlock is actually needed to configure multiple fields, in which case the encapsulation class--as exampled above--is usually the correct way to go.
The encapsulation class does not have to be a static inner class; it could be a separate class within your package, but the important part is that the DataBoundConstructor should take in an argument that matches the JSON structure being passed from the form.

Or you can add inline tag to optionalBlock like this:
<f:optionalBlock inline="true">
if inline is present, the foldable section will not be grouped into a separate JSON object upon submission.

Related

I can't implement a custom getter on realm result because `this(realmObject).propertyName` is null always yet data is there [duplicate]

It seems like my RealmObject values are being hidden by the RealmProxy class, but can be set from the proxyclass.
My model is pretty straight forward as you can see.
public class GroupRealm extends RealmObject {
#PrimaryKey
public String id;
#Index
public String name;
public String imageUrl;
public int order;
public GroupRealm parent;
public RealmList<GroupRealm> children;
public RealmList<ContentRealm> contents;
}
This is how i am setting the values(db is a valid Realm, and everything is in a transaction that commits fine):
GroupRealm gr = db.where(GroupRealm.class).equalTo("id",g.GroupID).findFirst();
if(gr==null){
gr = db.createObject(GroupRealm.class,g.GroupID);
}
gr.imageUrl = g.GlyphUrl;
gr.name = g.Title;
gr.order = g.OrderNum;
The image below is what I get when i query the db latter on.(same variable name not same place in code)
In my android.library where my RealmObjects are defined project I have the necessary plugins.
apply plugin: 'com.android.library'
apply plugin: 'realm-android'
and on the project level I am setting the correct dependencies:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath "io.realm:realm-gradle-plugin:0.90.1"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
I am out of ideas. If I try to access anything I retrieve the GroupRealm as expected but all of the public properties exposed through the proxy class return null!
Relevant FAQ in documentation: https://realm.io/docs/java/latest/#debugging
Realm uses Android Gradle Transform API. It gives a possibility to manipulate compiled class files before they are converted to dex files.
More details inside io.realm.transformer.RealmTransformer and io.realm.transformer. BytecodeModifier classes which can be found in the realm's github.
What RealmTransformer does, among others, is:
replacing all accesses to fields of user's RealmObjects with the appropriate Realm accessors.
You can also check result classes inside folder app/build/intermediates/transforms/RealmTransformer/
Example of setter:
Line of your code:
gr.imageUrl = g.GlyphUrl;
will be replaced with something like this:
String var5 = g.GlyphUrl;
gr.realmSet$imageUrl(var5);
Example of getter:
String url = gr.imageUrl;
will be replaced with something like this:
String url = gr.realmGet$imageUrl();
Example use case
You have created class GroupRealm. Realm using Transform API generates GroupRealmRealmProxy. This proxy class looks like this:
public class GroupRealmRealmProxy extends GroupRealm implements RealmObjectProxy, GroupRealmRealmProxyInterface {
private final GroupRealmRealmProxy.GroupRealmColumnInfo columnInfo;
private final ProxyState proxyState;
private RealmList<GroupRealm> childrenRealmList;
private RealmList<ContentRealm> contentsRealmList;
private static final List<String> FIELD_NAMES;
GroupRealmRealmProxy(ColumnInfo columnInfo) {
...
}
public String realmGet$id() {
this.proxyState.getRealm$realm().checkIfValid();
return this.proxyState.getRow$realm().getString(this.columnInfo.idIndex);
}
public void realmSet$id(String value) {
this.proxyState.getRealm$realm().checkIfValid();
if(value == null) {
this.proxyState.getRow$realm().setNull(this.columnInfo.idIndex);
} else {
this.proxyState.getRow$realm().setString(this.columnInfo.idIndex, value);
}
}
public String realmGet$name() {
this.proxyState.getRealm$realm().checkIfValid();
return this.proxyState.getRow$realm().getString(this.columnInfo.nameIndex);
}
public void realmSet$name(String value) {
this.proxyState.getRealm$realm().checkIfValid();
if(value == null) {
this.proxyState.getRow$realm().setNull(this.columnInfo.nameIndex);
} else {
this.proxyState.getRow$realm().setString(this.columnInfo.nameIndex, value);
}
}
...
}
You can observe that methods realmSet$name and realmGet$name don't have access to field name declared in the class GroupRealm. They use proxyState.
Now, let's back to the usage of GroupRealm. When you debug your code:
GroupRealm gr = db.where(GroupRealm.class).equalTo("id",g.GroupID).findFirst();
if(gr==null){
gr = db.createObject(GroupRealm.class,g.GroupID);
}
gr.imageUrl = g.GlyphUrl;
gr.name = g.Title;
gr.order = g.OrderNum;
in a reality it's decompiled version looks like this:
GroupRealm gr = (GroupRealm)realm.where(GroupRealm.class).equalTo("id", g.GroupId).findFirst();
if(gr == null) {
gr = (GroupRealm)realm.createObject(GroupRealm.class, g.GroupId);
}
String var7 = g.GlyphUrl;
gr.realmSet$imageUrl(var7);
var7 = g.Title;
gr.realmSet$name(var7);
int var8 = g.OrderNum;
gr.realmSet$order(var8);
First of all, gr is the instance of GroupRealmRealmProxy class. As you can see, setting of gr.name is replaced by gr.realmSet$name(var7). It means that the field name of GroupRealm is never used. The situation is analogous in the case of realmGet$.
While debugging you see your version of source code but actually you're using a modified version with injected methods realmSet$ and realmGet$.
The fields are null. You access the properties through a native method that replaces all field access. Previously (before 0.88.0) it used to create a dynamic proxy that overrode your getters and setters to use their native proxy implementation.
The fields don't have values. But as you can see, the Realm object has the values just fine: it says so in the toString() value.
There is nothing to be done about this. Because of the "clever" thing that Realm is doing, the debugger is completely prevented from doing what it is supposed to. You'll have to rely on a lot of Log.d statements.
I'm sorry. That's just the reality of it.
This is because of the Realm proxies model which is zero-copy storage.
You can use Kotlin Realm extension, Vicpinm library https://github.com/vicpinm/Kotlin-Realm-Extensions
If you still want to use in Java then you achieve it by:-
Realm.getDefaultInstance().copyFromRealm(realmObject)
The answers above are all right if you directly use an RealmObject retrieved from your Realm. With Managed RealmObject (Objects "directly" connected with your Realm, so the "Real Instance" of the object inside your Realm which you can Modify only inside RealmTransaction and which changes will affect all other Managed RealmInstance instantly) you can't see their values inside of the debugger because of the proxy.
Anyway you can work around this by using a NO MANAGED object, so by COPYING the RealmObject from the realm:
MyRealmObject obj = getRealmObjectFromRealm();
if(obj != null){
obj = mRealm.copyFromRealm(obj);
}
This way you will see all properties of your realm object inside the debugger.
Obviously if you need to use a Managed Realm Object inside your code, when you are debugging you need to change your code by creating another "MyRealmObject" instance which is a copy from the Realm of the other "MyRealmObject".
This way you will see all objects properties inside the debugger (:
Hope this is helpful,
Greetings & have a nice coding!
:D

check if a form field has changed using a play Form object

Is there any way to check directly, if the content of a form field in play framework has changed?
for example my Device.java is something like this:
class Device{
String name;
String type;}
and then somewhere in my controller, I have a form of type Device. is there any way to check using boundForm if the value of the name property has changed?
public class Devices extends Controller {
private static final Form<Device> deviceForm = Form.form(Device.class);
public static Result details(Device device) {
if (device == null) {
return notFound(String.format("Device does not exist. "));
}
Form<Device> filledForm = deviceForm.fill(device);
return ok(views.html.devices.details.render(filledForm));
}
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
...
[here]
...
}
}
note: details method will show the user the filled form, user may or may not change the values, and then by pressing a Save button , the save() method will be called.
In shortest words Form<T> isn't able to check if fields are changed it's just stateless between request and to check it you just need to get record from DB and compare field, by field.
Also you shouldn't rely on client-side validation as it's mainly for cosmetic, NOT for safety. Remember that it can be manipulated or omitted quite easy with common webdev tools.
Finally you shouldn't resign from Form validation possibilities,as it's very handy tool, instead you can cooperate with it, i.e. it can be something like:
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
if (boundForm.hasErrors()){
return badRequest(devices.details.render(boundForm));
}
Device boundDevice = boundForm.get();
Device existingDevice = Device.find.byId(boundDevice.id);
if (boundDevice.name.equals(existingDevice.name)){
boundForm.reject("Contents are identical");
return badRequest(devices.details.render(boundForm));
}
// else... form hasn't errors, name changed - should be updated...
boundDevice.update(boundDevice.id);
}
So you can display it in your view i.e.:
#if(yourForm.error("identicalContent")!=null) {
<div class="alert alert-danger">#yourForm.error("identicalContent").message</div>
}
As you can see from this sample - if you want just to skip UPDATE query if no changes - to save resources - it does not make sense, as you need to make SELECT query anyway to compare. In other cases (like i.e. additional logging ONLY if changed) above snippet is correct solution.

enuma label from message bundle

I have a enum with some entries for a selectOneMenu, that means the enum stucture looks like this: display, pdfLabel.
I want to load the entries label from my message bundle, that means depending on the locale.
It works fine, but only the first time after I depoly the project. That means, if the locale is "en" first time I load the entries, even after logout - session invalidate; if I change the locale to "de" the entries are still from the "en" - message. It works only if I redeploy.
Anyone has an idea about this behavior?
My enum:
public enum Transportmittel {
TRUCK(I18n.get("tv.moc.truck"), "TRUCK"),
AIRFREIGHT(I18n.get("tv.moc.airfreight"), "AIRFREIGHT"),
TRAIN(I18n.get("tv.moc.train"), "TRAIN"),
SEAFREIGHT(I18n.get("tv.moc.seafreight"), "SEAFREIGHT"),
BARGE(I18n.get("tv.moc.barge"), "BARGE");
String ausgabe;
String pdfLabel;
private Transportmittel(String ausgabe, String pdfLabel) {
this.ausgabe = ausgabe;
this.pdfLabel = pdfLabel;
}
public String toString() {
return ausgabe;
}
public String getLabelForPdf() {
return pdfLabel;
}
}
The controller where I load the entries:
#PostConstruct
public void init() {
transportMittelSelectList.add(new SelectItem(Transportmittel.TRUCK.pdfLabel, Transportmittel.TRUCK.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.TRAIN.pdfLabel, Transportmittel.TRAIN.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.AIRFREIGHT.pdfLabel, Transportmittel.AIRFREIGHT.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.SEAFREIGHT.pdfLabel, Transportmittel.SEAFREIGHT.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.BARGE.pdfLabel, Transportmittel.BARGE.ausgabe));
}
And this is where I load the message bundle:
public class I18n {
public static String get(String msg) {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle bundle = context.getApplication().getResourceBundle(
context, "messages");
return bundle.getString(msg);
}
}
The enum-values are static - so their constructor is called only once when loading the class by the classloader (=the first use). So at consecutive uses you still use the same instance containing the same string ausgabe set at construction-time during the first use.
So you have to set the values for ausgabe and pdfLabel when it is used. But maybe it is even better to have some "external" class which knows how to get the different labels for your enum-value instead of having these values somehow hard-coded inside the enum.
This is indeed not going to work. Enum properties are initialized only once, applicationwide, while i18n is essentially supposed to be resolved on a per-request basis.
You need to redesign your enum as such that only the label keys are hold instead of the resolved localized values.
TRUCK("tv.moc.truck", "TRUCK"),
AIRFREIGHT("tv.moc.airfreight", "AIRFREIGHT"),
TRAIN("tv.moc.train", "TRAIN"),
SEAFREIGHT("tv.moc.seafreight", "SEAFREIGHT"),
BARGE("tv.moc.barge", "BARGE");
And then provide the enum values as follows in an application scoped bean:
#ManagedBean
#ApplicationScoped
public class Data {
public Transportmittel[] getTransportmittels() {
return Transportmittel.values();
}
}
And then reference it in <f:selectItems> as follows (look, no need for SelectItem boilerplate):
<f:selectItems value="#{data.transportmittels}" var="transportmittel"
itemValue="#{transportmittel}" itemLabel="#{bundle[transportmittel.ausgabe]}" />
Or, if you happen to use JSF utility library OmniFaces already, as currently indicated in your user profile, then you could also bypass the whole application scoped Data bean and import it straight in the EL scope as follows:
<o:importConstants type="com.example.Transportmittels" /> <!-- can be declared in a master template -->
...
<f:selectItems value="#{Transportmittels}" var="transportmittel"
itemValue="#{transportmittel}" itemLabel="#{bundle[transportmittel.ausgabe]}" />
See also:
Localizing enum values in resource bundle
I had the same problem, but with ZK, I did need to fetch some properties to my enum, but it was blank String everytime.
To solve this you need to pass as the arguments the key of your property file in your enum constructor, like this:
After that in the get method of your enum propertie you must get the values in resource bundle and return them, like this:

Managed bean not retaining input values and returning null after packaging in separate JAR

I have a very strange behavior in a managed-bean where it is not retaining the values sent from the jsf and when it s going to process the POST then the properties are all null.
The JSF I have is a simple form with 2 fields and a button, the values of the two fields are received, and the button executes a POST method to process the data received from the JSF. When running a debug, I can see that after pressing the button then the setter methods are executed with the values sent to the bean (good), but when it goes to execute the mothod then suddenly all properties are null.
I have to include, that all this was working fine before, it started with this behavior when I moved all the managed-beans (backbeans) to a separated JAR file. I know that if I move the files again to the webapp then it will work, but I am looking for a way to not accumulate too many files in the same project, it's taking too long for compilation and deployment.
Here is the code of the backbean and the JSF:
#Named
#RequestScoped
public class RegisterController implements Serializable {
private String accountType;
public String getAccountTypes() {
return accountType;
}
public void setAccountTypes(String accountType) {
this.accountType = accountType; // Here it stores the value ********
}
private String businessType;
public String getBusinessType() {
return businessType;
}
public void setBusinessType(String businessType) {
this.businessType = businessType; // Here it stores the other value *******
}
// Method called with the button
public String prepareCreate() {
if ("PERSONAL".equals(getAccountTypes())) // Here is null!! *************
{
return "PersonalSignup";
}
else
if (businessType == null) // Here is also null!! ************
{
JsfUtil.addErrorMessage(
new Exception(""), ResourceBundle.getBundle(CommonUtil.bundleStr).getString("cc.signup.accounttype.invalid.businesstype"));
}
...
Any help would be really appreciated, thanks.
Found the answer here: How does JSF find beans annotated with #ManagedBean?. Certainly if you need that your webapp looks into your jar file for managed-beans, only include a faces-config.xml file in your META-INF folder of your jar file.

Java Annotations removing string literals from them?

Not sure if this is a decent question or not but here it goes. We are trying to implement a UI testing framework (selenium web-driver) and want to use a Page driven design for example
class HomePage {
#FindBy(how = How.Id, id="myPageHeaderID")
private String pageHeader
In the simple example above I need to hard-code the "myPageHeaderID" string literal. One of the requirements proposed is that we be able to pull in the "myPageHeaderID" from a property for both maintenance reasons (no code deploy if something changes) and for internationalization reasons. I have been searching around and probably not doing a proper search but is there any way of doing what I am asking above?
I briefly went down this route, but due to our application it wasn't quite achievable (pages aren't always displayed in the same order once you've visited a page).
public class PageElement implements WebElementAdapter, Locatable {
private How how;
private String using;
private boolean required;
#FindBy(how = How.ID_OR_NAME, using = DEFAULT_LOCATION_STRATEGY)
private WebElement backingElement;
public PageElement(How how, String using using) {
this.how = how;
this.using = using;
this.required = true;
}
/**
* This is how the overriding of the element location is done. I then injected
* these values in a spring configured bean file.
*
* This is needed on your config file:
* default-lazy-init="true" default-init-method="initialize">
*/
public final void initElement() {
if (backingElement == null || isStale() {
backingElement = getDriver().findElement(getLocationStrategy());
}
}
public By getLocationStrategy() {
By by = new ByIdOrName(using.replace(DEFAULT_LOCATION_STRATEGY, using));
switch(how) {
case CLASS_NAME:
by = By.className(using.replace(DEFAULT_LOCATION_STRATEGY, using));
break;
//Do for others
}
return by;
}
public WebElement getBackingElement() {
return backingElement;
}
}
public interface WebElementAdapter {
WebElement getBackingElement();
}
public interface Locatable {
By getLocationStrategy();
}
I then created common widgets in POJOs, and injected these into page objects which were a collection of these widgets.
From there I had a simple test harness which was responsible for taking in strings (which were then executed. Basically it allowed for test cases to be written in SpEL and act on the beans which were injected.
It was what I thought a pretty neat project, but I had to shelf it to get some other things done.
Annotations are essentially metadata. Taking database metadata for example, it would be weird if Oracle database would turn into MySQL, right? Here is the article about Annotation Transformers in TestNG. Didn't try it myself, but I think it could be implemented in some way or another.
AFAIK, you can call a method from the Annotation.
#FindBy(how = How.Id, id=getProp())
private String pageHeader;
private String getProp()
{
String prop = //whatever way you want to get the value
return prop;
}
Doesn't that work?

Categories