I am trying to add a unique validation on a field while adding a product on Broadleaf. Currently we have added a 'SKU' field while adding product from admin screen. I have used the following annotation to validate:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "blUniqueValueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
It works perfect when we try to add a new product.
But the problem is, If I try to update any product to change any field, it gives the same validation error
Looks like that doesn't work quite right, can you open an issue in https://github.com/BroadleafCommerce/Issues?
You can also write your own uniqueness validates that does not run into the same ID problem like so:
#Component
public class MyUniqueValueValidator implements PropertyValidator {
protected static final Log LOG = LogFactory.getLog(UniqueValueValidator.class);
#Override
public PropertyValidationResult validate(Entity entity,
Serializable instance,
Map<String, FieldMetadata> entityFieldMetadata,
Map<String, String> validationConfiguration,
BasicFieldMetadata propertyMetadata,
String propertyName,
String value) {
String instanceClassName = instance.getClass().getName();
DynamicEntityDao dynamicEntityDao = getDynamicEntityDao(instanceClassName);
List<Long> responseIds = dynamicEntityDao.readOtherEntitiesWithPropertyValue(instance, propertyName, value);
String message = validationConfiguration.get(ConfigurationItem.ERROR_MESSAGE);
if (message == null) {
message = entity.getType()[0] + " with this value for attribute " +
propertyName + " already exists. This attribute's value must be unique.";
}
boolean onlyInCurrentEntity = CollectionUtils.isEmpty(responseIds)
|| (responseIds.size() == 1 && responseIds.get(0).equals(getDynamicEntityDao(instanceClassName).getIdentifier(instance)));
return new PropertyValidationResult(onlyInCurrentEntity, message);
}
protected DynamicEntityDao getDynamicEntityDao(String className) {
return PersistenceManagerFactory.getPersistenceManager(className).getDynamicEntityDao();
}
}
And then use the validator by passing in the bean ID to the validationImplementation:
#AdminPresentationMergeOverride(name = "userSku", mergeEntries = #AdminPresentationMergeEntry(propertyType = PropertyType.AdminPresentation.VALIDATIONCONFIGURATIONS, validationConfigurations = {
#ValidationConfiguration(validationImplementation = "myUniqueValidator", configurationItems = {
#ConfigurationItem(itemName = "otherField", itemValue = "userSku") }) })
Related
Am trying to get data from 3 tables and every time I end up getting an error
Ljava.lang.Object; cannot be cast to .model.ISECO at
java.util.ArrayList.forEach
This are my entities
#Entity
public class IS01 {
private String IEA;
private String INUM;
private String ILINE;
private String I0103;
#Entity
public class ISOVER {
private String IEA;
private String ILINE;
private String INUM;
private String IRESULT;
private String ICON;
private String IBCON;
private String CASE;
private String RPTID
#Entity
public class POSTCO {
private String CEA;
private String CNUM;
private String CLINE;
private String PSCONTACT;
And this is my Repository
public interface LineSummary extends CrudRepository<ISOVER , String> {
#Query("select c.ILINE , c.IRESULT,e.PSCONTACT, \n" +
"c.ICON,c.IBCON, c.RPTID, c.CASE, d.i0103 as age\n" +
"FROM ISOVER c \n" +
"inner join IS01 d \n" +
"on c.IEA = d.IEA and c.INUM = d.INUM and c.ILINE = d.ILINE\n" +
"inner join POSTCO e on d.IEA = e.CEA and d.INUM = e.CNUM and d.ILINE = e.CLINE\n" +
"where c.CASE like %?1%")
Iterable<ISOVER> findEntriesByUserId(#Param("Case") String Case);
And this is my service
public ResponseEntity<Map<String, Object>> retrieveLineListingSQL(String Case){
Iterable <ISOVER > stud = lineSummary.findEntriesByUserId(Case);
Map<String, Object> parents = new HashMap<>();
parents.put("totalMembers", 9);
parents.put("questionaryinfo", new ArrayList<HashMap<String,Object>>());
ArrayList<HashMap<String,Object>> listings = (ArrayList<HashMap<String,Object>>) parents.get("questionaryinfo");
if (stud != null) {
stud.forEach(d -> {
HashMap<String,Object> entry = new HashMap<>();
entry.put("adultquestionary","Yes");
entry.put("caseNumber", d.getCASE());
listings.add(entry);
});
}
parents.put("DMStatus", "No review");
parents.put("ages", new HashMap<String, Object>());
return ResponseEntity.ok().body(parents);
}
How can I return the results from the query and map them accordingly?
I believe this is your culprit:
if (stud != null) {
stud.forEach(d -> {
HashMap<String, Object> entry = new HashMap<>(); // < -- here
entry.put("adultquestionary","Yes");
entry.put("caseNumber", d.getCASE());
listings.add(entry);
});
}
Have your tried using *.model.ISECO instead of java.lang.Object? Does that work, any particular limitation?
Additionally, you could refactor you code to something way more simple, if you follow the same explanation provided in here: How to make nested JSON response with Array from a Stored procedure
Create a response model that outputs the format you expect as response.
There is no need for you to do all that collections handling one-by-one. The representation of an object in JSON is a MAP, basically let the
Jackson JSON library do all that work for you.
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
I have a project that currently uses Spring Cloud Streams and RabbitMQ underneath. I've implemented a logic based on the documentation. See below:
#Component
public class ReRouteDlq {
private static final String ORIGINAL_QUEUE = "so8400in.so8400";
private static final String DLQ = ORIGINAL_QUEUE + ".dlq";
private static final String PARKING_LOT = ORIGINAL_QUEUE + ".parkingLot";
private static final String X_RETRIES_HEADER = "x-retries";
private static final String X_ORIGINAL_EXCHANGE_HEADER = RepublishMessageRecoverer.X_ORIGINAL_EXCHANGE;
private static final String X_ORIGINAL_ROUTING_KEY_HEADER = RepublishMessageRecoverer.X_ORIGINAL_ROUTING_KEY;
#Autowired
private RabbitTemplate rabbitTemplate;
#RabbitListener(queues = DLQ)
public void rePublish(Message failedMessage) {
Map<String, Object> headers = failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
if (retriesHeader == null) {
retriesHeader = Integer.valueOf(0);
}
if (retriesHeader < 3) {
headers.put(X_RETRIES_HEADER, retriesHeader + 1);
String exchange = (String) headers.get(X_ORIGINAL_EXCHANGE_HEADER);
String originalRoutingKey = (String) headers.get(X_ORIGINAL_ROUTING_KEY_HEADER);
this.rabbitTemplate.send(exchange, originalRoutingKey, failedMessage);
}
else {
this.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
#Bean
public Queue parkingLot() {
return new Queue(PARKING_LOT);
}
}
It does what it is expected, however, it is binded to RabbitMQ, and my company is planning to stop using this message broker in one year or two (don't know why, must be some crazy business). So, I want to implement the same thing, but detach it from any message broker.
I tried changing the rePublish method this way, but it does not work:
#StreamListener(Sync.DLQ)
public void rePublish(Message failedMessage) {
Map<String, Object> headers = failedMessage.getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
if (retriesHeader == null) {
retriesHeader = Integer.valueOf(0);
}
if (retriesHeader < 3) {
headers.put(X_RETRIES_HEADER, retriesHeader + 1);
String exchange = (String) headers.get(X_ORIGINAL_EXCHANGE_HEADER);
String originalRoutingKey = (String) headers.get(X_ORIGINAL_ROUTING_KEY_HEADER);
this.rabbitTemplate.send(exchange, originalRoutingKey, failedMessage);
}
else {
this.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
It fails because the Message class has immutable Headers - throws exception on the put attempt saying you can't change its values (uses org.springframework.messaging.Message class).
Is there a way to implement this dead-letter queue handler in a message broker independent way?
Use
MessageBuilder.fromMessage(message)
.setHeader("foo", "bar")
...
.build();
Note that the message in #StreamListener is a spring-messaging Message<?>, not a spring-amqp Message and can't be sent using the template that way; you need an output binding to send the message to.
I have a JSP page wherein user has to enter some custom URL. I want to pass that custom url in #WebInitParam in my servlet
#WebServlet(name = "oauthCustomURL", initParams = {
#WebInitParam(name = "clientId", value = "123"),
#WebInitParam(name = "key", value = "***"),
#WebInitParam(name = "environment", value = "customUrl"),
}) //in value I want to pass the value entered by user
#WebInitParam's are used for the configuration of servlets implemented by third-party libraries. Usually, these libraries use methods getInitParameterNames() and getInitParameter() of abstract class GenericServlet (but you should check in library code for it).
For the dynamic setting of the #WebInitParam you can override those methods in your servlet implementation. Below is an example of how to do it.
#WebServlet(urlPatterns = "/abc/*")
public class DynamicInitParamServlet extends SomeCustomLibraryHttpServlet {
private static final String WEB_INIT_PARAM_NAME = "some.param.name";
private Integer webInitParamValue = null;
#Override
public void init(ServletConfig config) throws ServletException {
// calculate init param value dynamically,
// TODO: implement your own code
webInitParamValue = 2 * 3;
// call custom library servlet init after init parameter value is set
super.init(config);
}
#Override
public Enumeration<String> getInitParameterNames() {
if (webInitParamValue != null) {
final Set<String> initParameterNames = new HashSet<>(Collections.list(super.getInitParameterNames()));
initParameterNames.add(WEB_INIT_PARAM_NAME);
return Collections.enumeration(initParameterNames);
} else {
return super.getInitParameterNames();
}
}
#Override
public String getInitParameter(String name) {
if (WEB_INIT_PARAM_NAME.compareTo(name) == 0 && webInitParamValue != null) {
return "" + webInitParamValue;
} else {
return super.getInitParameter(name);
}
}
}
Below is my test method.
#Test
public void testSaveUserPreference() throws Exception {
final long userId = 1L;
final String category = "category";
final UserPreference userPreference = createMockedUserPreference(userId, category);
final UserPreferenceDTO userPreferenceDTO = createMockedUserPreferenceDTO(userId, category);
final Date lastAccessed = userPreference.getLastAccessedDate();
userPreferenceDTO.setLastAccessedDate(lastAccessed);
//override the convert method to control the object being "saved"
final UserPreference document = new UserPreference();
new MockUp<UserPreferenceUtils>() {
#Mock
UserPreference convertFromDTO(UserPreferenceDTO dto) {
document.setUserId(userPreference.getUserId());
document.setCategory(userPreference.getCategory());
document.setProperties(userPreference.getProperties());
return document;
}
};
new Expectations() {{
component.getUserPreference(userId, category);
returns(userPreference);
component.saveUserPreference(userPreference);
returns(userPreference);
}};
UserPreferenceDTO actual = service.savePreference(userPreferenceDTO);
assertNotNull(actual);
assertEquals(userPreference.getUserId(), actual.getUserId());
assertEquals(userPreference.getCategory(), actual.getCategory());
assertEquals(userPreference.getProperties(), actual.getProperties());
assertNotNull(actual.getCreatedDate());
assertTrue(lastAccessed.before(actual.getLastAccessedDate()));
}
Below is the service method in which I am facing the error.
#Transactional
public UserPreferenceDTO savePreference(UserPreferenceDTO userPreference) {
UserPreference preference = UserPreferenceUtils.convertFromDTO(userPreference);
UserPreference existingPreference = userPreferenceComponent.getUserPreference(userPreference.getUserId(), userPreference.getCategory());
if(existingPreference!=null && !CollectionUtils.isEmpty(existingPreference.getProperties())) {
Map<String,Object> report = (Map<String, Object>) preference.getProperties().get("favoriteFilters");
Map<String,Object> existingRep = (Map<String, Object>) existingPreference.getProperties().get("favoriteFilters");
existingRep.putAll(report);
existingPreference.getProperties().put("favoriteFilters",existingRep);
} else {
existingPreference = preference;
}
if (existingPreference.getCreatedDate() == null) {
existingPreference.setCreatedDate(new Date());
}
existingPreference.setLastAccessedDate(new Date());
UserPreferenceDTO savedPreference = UserPreferenceUtils.convertFromDocument(userPreferenceComponent.saveUserPreference(existingPreference));
return savedPreference;
}
When calling the save method in the second last line, it is giving this error.
mockit.internal.UnexpectedInvocation: Parameter "userPreference" of com.curaspan.platformsupportservice.components.preference.UserPreferenceComponent#saveUserPreference(com.curaspan.platformsupportservice.mongo.document.UserPreference userPreference) expected com.curaspan.platformsupportservice.mongo.document.UserPreference#3d3fcdb0, got com.curaspan.platformsupportservice.mongo.document.UserPreference#636be97c
The userPreference object in the Expectation and the actual method is not matching, though I have all the parameters same. Is there anything I am missing out?