Missing attributes with Spring LDAP object-directory mapper annotations - java

I am trying to use Spring LDAP's object-directory mapping to write an object to an LDAP server. The object is annotated with #Entity and several fields are annotated with #Attribute.
As long as all of the annotated fields are populated, everything works. But if the value of a field, say myattribute, is null or an empty string, the create and update methods of LdapTemplate throw errors. The server rejects the operation, with the complaint "Attribute value '' for attribute 'myattribute' is syntactically incorrect"
The LDAP schema permits 'myattribute' to be missing (it is a "may" attribute for the relevant objectclass), but if it is present, it is not permitted to be blank (it has Directory String syntax). I cannot change the schema.
Is there some way to get Spring LDAP to omit 'myattribute' when the corresponding POJO field is null or empty, rather than attempting to create the attribute with a blank value?

I have found a solution, which may not be the most elegant for my application, but it works. Instead of declaring the Java field to be type String, declare it to be type List. Then, in the setter, if the value is blank or null, I set the list length to zero instead of setting a single empty value.
#Entry( objectClasses={"myObject"} )
public class MyDataContainer {
#Attribute("myattribute")
private List<String> _myattribute = new ArrayList<String>(1);
public String getMyAttribute() {
if ( _myattribute.length() > 0 ) {
return _myattribute.get(0);
}
return null;
}
public void setMyAttribute( String value ) {
_myattribute.clear();
value = ( value == null ) ? "" : value.trim();
if ( ! "".equals( value ) ) {
_myattribute.add( value );
}
}
}

Related

Why does overriding the jOOQ strategy break copying from POJOs to Records?

We use the https://github.com/etiennestuder/gradle-jooq-plugin for generating jOOQ classes on-demand from our database (DDL, .sql file with CREATE TABLE statements). However, we noted that when overriding the strategy.name in jooq.configurations.main.generationTool.generator, we started seeing peculiar behavior.
The database schema
We have some field names like _is_tombstone, _revision etc in our tables. These are for "metadata" which differs from the normal "data" columns (data coming from another system vs "metadata" logging event details about why the change was triggered).
Other tables like id, name etc do not have any special prefix.
The generator
Here is our current generator strategy, inspired by https://www.jooq.org/doc/latest/manual/code-generation/codegen-generatorstrategy/
public class CustomNameGeneratorStrategy extends DefaultGeneratorStrategy {
#Override
public String getJavaMemberName( Definition definition, Mode mode ) {
String memberName = super.getJavaMemberName( definition, mode );
// Converts e.g. _IsTombstone to _isTombstone
if ( memberName.startsWith( "_" ) ) {
memberName = "" + memberName.charAt( 0 ) + toLowerCase( memberName.charAt( 1 ) ) + memberName.substring( 2 );
}
return memberName;
}
#Override
public String getJavaGetterName( Definition definition, Mode mode ) {
String methodName = super.getJavaGetterName( definition, mode );
methodName = methodName.replace( "_", "" );
// isTombstone() seems more natural than getIsTombstone()
methodName = methodName.replace( "getIs", "is" );
return methodName;
}
#Override
public String getJavaSetterName( Definition definition, Mode mode ) {
String methodName = super.getJavaSetterName( definition, mode );
return methodName.replace( "_", "" );
}
}
The problem
With the above in place, the code below causes some very nasty bugs with certain fields not copied from the POJO to the Record instance. Only id, name etc works. All fields which have underscores in the name get excluded in the copying; they have null values in the target Record instance.
This, in turns, makes the DB insertion fail since certain mandatory fields do not have any values.
import static some.package.Tables.GROUPS;
import some.package.tables.records.GroupRecord;
import some.package.tables.pojos.TSDBGroup;
// ...
TSDBGroup tsdbGroup = createTsdbGroup(...);
// The GroupRecord here becomes a "partial copy".
GroupRecord groupRecord = create.newRecord(GROUPS, tsdbGroup);
groupRecord.store();
Why does this happen?
The problem turned out to be that jOOQ expects one of the following to hold true, unless you annotate your getters/setters with JPA-style annotations:
Method names for field setters use the standard name (setFoo for a DB column named foo or FOO), or
Field names use the standard name (foo for a DB column named foo or FOO)
Break both of these rules, and you keep to keep the pieces. :-)
More specifically, this is caused by the following code in jOOQ:
// No annotations are present
else {
members = getMatchingMembers(configuration, type, field.getName(), true);
method = getMatchingGetter(configuration, type, field.getName(), true);
}
// Use only the first applicable method or member
if (method != null)
Tools.setValue(record, field, method.invoke(source));
else if (members.size() > 0)
setValue(record, source, members.get(0), field);
}
Workaround
The simple workaround is to remove the getJavaMemberName method in the custom GeneratorStrategy. jOOQ is then able to populate the fields as expected when creating Records from POJOs.

How to handle null values in an HQL query when checking a list for a contained integer value

What I have is a Spring Boot repository in which I try to create a query function using HQL.
The function takes an Integer parameter which should be ignored by the query if it is null, else a list should be checked if it contains the value.
I have the where clause in two parts, the null check and the list check, looking like this:
#Query("SELECT u FROM MyEntity " +
" WHERE (:myParam is null or :myParam in (2, 3))"
)
Now the problem is that for the :myParam in (2, 3) part it is complaining "Inconsistent Datatypes: expected BINARY got NUMBER
(when :myParam is null, for :myParam != null it works)
I tried:
casting either the param or the null value to in
using coalesce(:myParam, CAST(NULL AS int)) which worked on a similar problem with :myParam being an integer list
using a switch case statement
(case when :spracheKy is null then true when :spracheKy in (2, 3) then true else false end) = true
Thanks in advance for any help
why don't you just make use of two different repository methods, e.g. one that takes no parameter and another method that takes the parameter. and then decide and encapsulate the decision taking logic in a separate method of your service layer - I mean the logic which repository method to call based on parameter being null or not... could look like this:
#Service
#Transactional
public class YourService {
// autowired by constructor injection
private final YourEntityRepository repo;
public List<YourEntity> getAllYourEntitiesByParam(Long param) {
if (param == null) {
return repo.findAll();
}
return repo.findAllByParam(param);
}
}

Apache Commons Configuration validate properties file

I am using Apache Commons Configuration library with PropertiesConfiguration.
My application loads the config file right after its started, like this:
public PropertiesConfiguration loadConfigFile(File configFile) throws ConfigurationNotFoundException {
try {
if (configFile != null && configFile.exists()) {
config.load(configFile);
config.setListDelimiter(';');
config.setAutoSave(true);
config.setReloadingStrategy(new FileChangedReloadingStrategy());
setConfigLoaded(true);
}
else {
throw new ConfigurationNotFoundException("Configuration file not found.");
}
} catch (ConfigurationException e) {
logger.warn(e.getMessage());
setDefaultConfigValues(config);
config.setFile(configFile);
}
return config;
}
My question is, how can I validate the configFile, so I can be sure that no property in that file is missing and later in my code I won't get a NullPointerException when trying to access the properties, e.g.:
PropertiesConfiguration config = loadConfig(configFile);
String rootDir = config.getString("paths.download"); // I want to be sure that this property exists right at the app start
I didn't found anything in the documentation or google, just something about XML validation.
The goal is to provide feedback to the user at program start that the configuration file is corrupted.
There is no build-in mechanism for properties-file?
What is a configuration object supposed to do if you pass in a key to one of its get methods that does not map to an existing property?
the default behavior as implemented in AbstractConfiguration is to return null if the return value is an object type.
For primitive types as return values returning null (or any other special value) is not possible, so in this case a NoSuchElementException is thrown
// This will return null if no property with key "NonExistingProperty" exists
String strValue = config.getString("NonExistingProperty");
// This will throw a NoSuchElementException exception if no property with
// key "NonExistingProperty" exists
long longValue = config.getLong("NonExistingProperty");
For object types like String, BigDecimal, or BigInteger this default behavior can be changed:
If the setThrowExceptionOnMissing() method is called with an argument of true, these methods will behave like their primitive counter parts and also throw an exception if the passed in property key cannot be resolved.
Situation is little tricky for Collection & array types as they will return empty collection or array.

Trying to insert defaltValue to a column through impex but getting error unable to insert default value in the place of null

Null value in second column (incoming csv file):->
input CSV: 10512,,
10513,12345,
impex:
INSERT_UPDATE Product;code[unique=true];vendors(code)[translator=ca.batch.converter.StiboSetDefaultVendorIfNullTranslator];...
code:
Extending de.hybris.platform.impex.jalo.translators.AbstractValueTranslator;
private final String defaultVendorCode = "000000";
#Override
public Object importValue(String valueExpr, final Item item)
throws JaloInvalidParameterException {
if (valueExpr == null || StringUtils.isEmpty(valueExpr)) {
LOG.debug("Current attribute value is null so inserting "
+ defaultVendorCode);
valueExpr = defaultVendorCode;
}
return valueExpr;
}
getting the same below error here also for the 12345 but final impex conveterd row has the number (impex row -> 10153;12345)
due to Argument mismatch trying to set value '000000' for attribute de.hybris.platform.jalo.product.Product.vendors (got java.lang.String, expected de.h
ybris.platform.jalo.product.Product).,
(impex row -> 10153;;)
You should try this:[allownull=true] attribute modifier;
Your impex should look like this:
INSERT_UPDATE Product;code[unique=true];vendors(code)[allownull=true]
Only Import
true / false
Default: false
If set to true, this modifier explicitly allows null values for the column values. If there is no business code that blocks null values, this modifier even allows null values in mandatory attributes, such as the catalogVersion attribute of the Media type, for example.
Example:
[allownull=true]
Tip
In the Service Layer mode, import may fail if allownull is set. Since hybris Commerce Suite version 5.1.1, import will switch dynamically to the legacy mode if it encounters this parameter. After processing a given line, the import will switch back to the SL mode.
I think the error message is quite clear on this:
(got java.lang.String, expected de.h ybris.platform.jalo.product.Product).,
For the translator you'd have to lookup the actual default vendor object instead of returning the default vendor code.
I think the easiest solution would be if you used a Decorator instead that then returns the code values of your "vendors" attribute.
You can find detailed instructions here:
https://wiki.hybris.com/display/release5/ImpEx+API#ImpExAPI-WritingOwnCellDecorator
but basically something like this:
public class MyDecorator implements CSVCellDecorator
{
public String decorate( int position, Map<Integer, String> srcLine )
{
// here add your custom logic to check and if applies return your default vendor code, otherwise return the given input value
//String parsedValue=srcLine.get(position);
//return parsedValue+"modified"; // some decoration stuff
}
}
Hope that helps a bit :)

Does Spring BindingResult or FormError clear a field that has an error?

I have checked both the BindingResult and FieldError Javadocs (and source) and can't find a definite answer to this question. When the following code is executed in a Spring MVC Controller method (on a POST), the field that fails validation is displayed as blank when the form is redisplayed. Is this behavior intended? Can I depend on it in all cases (fields that fail validation are returned as blank?)
// Does the user's email address already exist?
if (theUser != null) {
result.addError(new FieldError("theForm", "emailAddress", null, false, new String[] { "theAccount.emailAddress.alreadyInUse" }, null, null));
return ".createAccount";
}
Check again FieldError constructor, according to JavaDocs, 3rd parameter is rejected field value:
rejectedValue - the rejected field value
The exact part of code which overrides value is in AbstractBindingResult class:
public Object getFieldValue(String field) {
FieldError fieldError = getFieldError(field);
// Use rejected value in case of error, current bean property value else.
Object value = (fieldError != null ? fieldError.getRejectedValue() :
getActualFieldValue(fixedField(field)));
// Apply formatting, but not on binding failures like type mismatches.
if (fieldError == null || !fieldError.isBindingFailure()) {
value = formatFieldValue(field, value);
}
return value;
}
So while you provide FieldError class with null rejectedValue, the form field is cleared. As or me, I've always used rejectValue instead of addError:
result.rejectValue( "field", "errorCode" );

Categories