Spring MVC - Search using Param - database

I want to search something from database example i have three textfields
First Name , Last Name and Gender
if i input there
Some Firstname , Some Lastname , Male I want to search all the people with that certain name and gender and display it how do I do this?
Note: I am just new to Spring and just started learning thanks
Controller:
#RequestMapping(value = "/student/search", method = RequestMethod.GET)
public String searchStudent(#Param("name") String name, #Param("type") String type, Map<String, Object> model) {
Map<String, String> params = new HashMap<String, String>();
params.put("name", name);
params.put("type", gender);
model.put("students",studentRepository.findAll(StudentSpecification.search(params)));
return "/students/list";
}
}
Specification:
public static Specification<Student> search(final Map<String, String> params) {
return (root, query, criteriaBuilder) -> {
List<Predicate> predicates = new ArrayList<Predicate>();
params.forEach((k, v) -> {
if (StringUtils.equals(k, "name")) {
if (StringUtils.isNotBlank(v)) {
Predicate hasFirstName =
criteriaBuilder.like(root.join("user").<String>get("firstName"), "%" + v + "%");
Predicate hasLastName =
criteriaBuilder.like(root.join("user").<String>get("lastName"), "%" + v + "%");
predicates.add(criteriaBuilder.or(hasFirstName, hasLastName));
}
}
});
return criteriaBuilder.and(predicates.toArray(new Predicate[] {}));
};
}
}

I presume from your code that you are using Spring data jpa, with specification, right?
if so, you need to generate MetaModel of your entities, add the code below if you are using maven, and this will do it for you
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<version>2.0.5</version>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<processors>
<processor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</processor>
</processors>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>4.3.6.Final</version>
</dependency>
</dependencies>
</plugin>
I suppose you have an entity called Student, this plugin declaration will generate a classe suffixed with underscore 'Student_'
and then you could implement the specification interface, something like this
public class StudentSpecification {
public static Specification<Student> search(final Map<String, String> params) {
return new Specification<Student>() {
#Override
public Predicate toPredicate(Root<Student> studentRoot, CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate res = null;
if(params.containKey("name"))
res = cb.equal(studentRoot.get(Student_.name), params.get("name"));
if(params.containKey("type"))
if(res ==null)
res = cb.equal(studentRoot.get(Student_.name), params.get("type"));
else
res = cb.and(res, cb.equal(studentRoot.get(Student_.name), params.get("type")));
return res;
}
}
}
and of course your StudentRepository class should extends JpaSpecificationExecutor, something like this
interface StudentRepository extends JpaSpecificationExecutor<Student> {
}

You can do this using #Query, add method in the repository:
#Query("SELECT e FROM STUDENT e WHERE e.name = ?1 AND e.type = ?2")
public List<student> findAll(String name, String type);
Use that method in the controller:
#RequestMapping(value = "/student/search", method = RequestMethod.GET)
public String searchStudent(#Param("name") String name, #Param("type") String type, Map<String, Object> model) {
model.put("students",studentRepository.findAll(name, type));
return "/students/list";
}

Related

How to create Liquibase changesets automatically?

I want to generate a changelog file automatically via one of the maven liquibase plugin commands. The changelog should include a changeset for creating a database table for the DiscountCode entity. Would diff be the correct plugin command for this case?
#Entity
#Table(name = "discount_code")
public class DiscountCode {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long discountId;
private String discountCode;
private LocalDate expiration;
private BigDecimal discountValue;
public DiscountCode() {}
public DiscountCode(String discountCode, LocalDate expiration, BigDecimal discountValue) {
this.discountCode = discountCode;
this.expiration = expiration;
this.discountValue = discountValue;
}
public Long getDiscountId() {
return discountId;
}
public void setDiscountId(Long discountId) {
this.discountId = discountId;
}
public String getDiscountCode() {
return discountCode;
}
public void setDiscountCode(String discountCode) {
this.discountCode = discountCode;
}
public LocalDate getExpiration() {
return expiration;
}
public void setExpiration(LocalDate expiration) {
this.expiration = expiration;
}
public BigDecimal getDiscountValue() {
return discountValue;
}
public void setDiscountValue(BigDecimal discountValue) {
this.discountValue = discountValue;
}
#Override
public String toString() {
return (
"DiscountCode{" +
"discountId=" +
discountId +
", discountCode='" +
discountCode +
'\'' +
", expiration=" +
expiration +
", discountValue=" +
discountValue +
'}'
);
}
}
These are the available liquibase commands:
The solution is using the Liquibase Hibernate plugin.
The plugin configuration:
<plugins>
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>3.4.1</version>
<configuration>
<propertyFile>src/main/resources/liquibase.properties</propertyFile>
</configuration>
<dependencies>
<dependency>
<groupId>org.liquibase.ext</groupId>
<artifactId>liquibase-hibernate4</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
</dependencies>
</plugin>
</plugins>
Liquibase properties:
changeLogFile=classpath:liquibase-changeLog.xml
url=jdbc:mysql://localhost:3306/oauth_reddit
username=tutorialuser
password=tutorialmy5ql
driver=com.mysql.jdbc.Driver
referenceUrl=hibernate:spring:org.baeldung.persistence.model
?dialect=org.hibernate.dialect.MySQLDialect
diffChangeLogFile=src/main/resources/liquibase-diff-changeLog.xml
View this page for further information: https://www.baeldung.com/liquibase-refactor-schema-of-java-app

Spring MVC REST api not consuming json in chrome web client

I have an angularjs spring web app that returns a json. This is the url
http://localhost:8080/AngularJSPostFormSpringMVC/
The above is the output of the json url.
When I luanch the url in chrome web client it does not consume json even though it returns a status of 200 : 0k.
This is the out put
This is my controller code
public class SpringMVCController {
#RequestMapping(value = "/PostFormData", method = RequestMethod.POST)
public #ResponseBody
Person PostService(#RequestBody Person person) {
return person;
}
#RequestMapping(value = "/PostFormDataByParam", method = RequestMethod.POST)
public #ResponseBody Person PostFormDataByParam(HttpServletRequest request) {
Person person = new Person();
person.setName(request.getParameter("name"));
person.setName(request.getParameter("location"));
person.setName(request.getParameter("phone"));
return person;
}
Please what could be wrong?
create model
public class Person {
private int name;
private String location;
private String phone;
getter setter ...
dependencies
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.3</version>
</dependency>
controller
#RequestMapping(value = "/PostFormDataByParam", method = RequestMethod.POST)
public #ResponseBody Person PostFormDataByParam(#RequestBody Person person, HttpServletRequest request) {
person.get ...
return person;
}
good luck ~
Change type of parameter define in method PostService, like this;)
#RequestMapping(value = "/PostFormData", method = RequestMethod.POST, produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody
Person PostService(#RequestBody Person[] person) {
return person[0];
}

Jackson confused with bidirectional one-to-many relationship : failed to lazily initialize collection

Backend Spring MVC and Spring-data,spring-security . Front end Angularjs.I'm using spring 3.1 ;Jackson 1.8 ; JPa 2.1 ands mysql.The basic problem is the same as asked multiple time. I have a simple program with two POJOs sites and typeSite- where a typeSite can have multiple Sites. But I'm getting the following error :
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: TypeSites.sitees, could not initialize proxy - no Session (through reference chain: vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]-model.TypeSites["sitees"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: model.TypeSites.sitees, could not initialize proxy - no Session (through reference chain:vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]->model.TypeSites["sitees"])
Caused by: com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: model.TypeSites.sitees, could not initialize proxy - no Session (through reference chain: vo.TypeSitesListVO["typesites"]->java.util.UnmodifiableRandomAccessList[0]->model.TypeSites["sitees"])
And this following error in browser :
Failed to load resource: the server responded with a status of 500 (Internal Server Error)
So to understand better this error let us see how the JPA/Hibernate handles the relationship.
Every time we do a query in the database the JPA will bring to all information of that class. The exception to this rule is when we talk about list (collection).
Notice in the above code, that the database query will return a Sitesobject. When i access the site collection, the container will notice that the site collection is a lazy attribute and it will “ask” the JPA to load this collection from the database.
In the moment of the query (that will bring the site collection) execution, an exception will happen. When the JPA/Hibernate tries to access the database to get this lazy information, the JPA will notice that there is no opened collection. That is why the exception happens, the lack of an opened database connection.
model class :
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Sites implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private int id;
private TypeSites siteesTypeSite;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
//#ManyToOne(fetch = FetchType.LAZY)
//#JoinColumn(name ="idTypeSite")
//#JsonIgnore
#JsonBackReference("site-typeSite")
#ManyToOne
#JoinColumn(name = "idTypeSite", foreignKey = #ForeignKey(name = "fk_site_typeSite"))
public TypeSites getSiteesTypeSite() {
return siteesTypeSite;
}
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="idTypeSite")
public class TypeSites implements java.io.Serializable {
private int idTypeSite;
private Set<Sites> sitees= new HashSet<Sites>(0);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getIdTypeSite() {
return idTypeSite;
}
//#JsonSerialize(using = CustomListSerializer.class)
//#JsonView(Views.Internal.class)
//#JsonIgnore
#JsonManagedReference("site-typeSite")
#OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
public Set<Sites> getSitees() {
return sitees;
}
code of value object design pattern
public class TypeSitesListVO {
private int pagesCount;
private long totalTypeSite;
private String actionMessage;
private String searchMessage;
private List<TypeSites> typesites;
//setters/gtters}
public class SitesListVO {
private int pagesCount;
private long totalSites;
private String actionMessage;
private String searchMessage;
private List<Sites> sites = new ArrayList<Sites>();
//setters/gtters}
Repository :
public interface SitesRepository extends PagingAndSortingRepository<Sites, Integer> {
//#Query("SELECT s FROM Sites s TypeSites ts JOIN FETCH s.siteesTypeSite WHERE s.id =ts.idTypeSite ")
//#EntityGraph(value = "sites.type", type = EntityGraphType.LOAD)
Page<Sites> findBycodeGSMLike(Pageable pageable, String codeGSM);
//Page<Sites> findBycodeGSMLike(Pageable pageable, List<String> codeGSM);
}
Services class :
#Service
#Transactional
public class SitesService {
#Autowired
private SitesRepository siteRepository;
#Transactional(readOnly = true)
public SitesListVO findAll(int page, int maxResults) {
Page<Sites> result = executeQueryFindAll(page, maxResults);
if(shouldExecuteSameQueryInLastPage(page, result)){
int lastPage = result.getTotalPages() - 1;
// for (Sites e: result){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
result = executeQueryFindAll(lastPage, maxResults);
}
return buildResult(result);
}
public void save(Sites site) {
siteRepository.save(site);
}
#Transactional(readOnly = true)
public SitesListVO findBycodeGSMLike(int page, int maxResults, String codeGSM) {
Page<Sites> result = executeQueryFindByName(page, maxResults, codeGSM);
if(shouldExecuteSameQueryInLastPage(page, result)){
int lastPage = result.getTotalPages() - 1;
// for (Sites e: result){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
result = executeQueryFindByName(lastPage, maxResults, codeGSM);
}
return buildResult(result);
}
private boolean shouldExecuteSameQueryInLastPage(int page, Page<Sites> result) {
return isUserAfterOrOnLastPage(page, result) && hasDataInDataBase(result);
}
private Page<Sites> executeQueryFindAll(int page, int maxResults) {
final PageRequest pageRequest = new PageRequest(page, maxResults, sortBycodeGSMASC());
// Page<Sites> SitesList = siteRepository.findAll(pageRequest);
// for (Sites e: SitesList){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
// return SitesList;
return siteRepository.findAll(pageRequest);
}
private Sort sortBycodeGSMASC() {
return new Sort(Sort.Direction.ASC, "codeGSM");
}
private SitesListVO buildResult(Page<Sites> result) {
return new SitesListVO(result.getTotalPages(), result.getTotalElements(), result.getContent());
}
private Page<Sites> executeQueryFindByName(int page, int maxResults, String codeGSM) {
final PageRequest pageRequest = new PageRequest(page, maxResults, sortBycodeGSMASC());
// Page<Sites> SitesList = siteRepository.findBycodeGSMLike(pageRequest, codeGSM);
// for (Sites e: SitesList){
// Hibernate.initialize(e.getSiteesTypeSite());
// }
// return SitesList;
return siteRepository.findBycodeGSMLike(pageRequest, codeGSM);
}
private boolean isUserAfterOrOnLastPage(int page, Page<Sites> result) {
return page >= result.getTotalPages() - 1;
}
private boolean hasDataInDataBase(Page<Sites> result) {
return result.getTotalElements() > 0;
}
}
controller class :
#Controller
#RequestMapping(value = "/protected/sites")
public class SitesController {
private static final String DEFAULT_PAGE_DISPLAYED_TO_USER = "0";
#Autowired
private SitesService siteService;
#Autowired
private MessageSource messageSource;
#Value("5")
private int maxResults;
#RequestMapping(method = RequestMethod.GET)
public ModelAndView welcome() {
return new ModelAndView("sitesList");
}
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<?> listAll(#RequestParam int page, Locale locale) {
return createListAllResponse(page, locale);
}
#RequestMapping(method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> create(#ModelAttribute("site") Sites site,
#RequestParam(required = false) String searchFor,
#RequestParam(required = false,
defaultValue = DEFAULT_PAGE_DISPLAYED_TO_USER) int page,
Locale locale) {
siteService.save(site);
if (isSearchActivated(searchFor)) {
return search(searchFor, page, locale, "message.create.success");
}
return createListAllResponse(page, locale, "message.create.success");
}
private SitesListVO listAll(int page) {
return siteService.findAll(page, maxResults);
}
private ResponseEntity<SitesListVO> returnListToUser(SitesListVO siteList) {
return new ResponseEntity<SitesListVO>(siteList, HttpStatus.OK);
}
private ResponseEntity<?> createListAllResponse(int page, Locale locale) {
SitesListVO siteListVO = listAll(page);
return createListAllResponse(page, locale, null);
}
private ResponseEntity<?> createListAllResponse(int page, Locale locale, String messageKey) {
SitesListVO siteListVO = listAll(page);
addActionMessageToVO(siteListVO, locale, messageKey, null);
return returnListToUser(siteListVO);
}
private SitesListVO addActionMessageToVO(SitesListVO siteListVO, Locale locale, String actionMessageKey, Object[] args) {
if (StringUtils.isEmpty(actionMessageKey)) {
return siteListVO;
}
siteListVO.setActionMessage(messageSource.getMessage(actionMessageKey, args, null, locale));
return siteListVO;
}
private SitesListVO addSearchMessageToVO(SitesListVO siteListVO, Locale locale, String actionMessageKey, Object[] args) {
if (StringUtils.isEmpty(actionMessageKey)) {
return siteListVO;
}
siteListVO.setSearchMessage(messageSource.getMessage(actionMessageKey, args, null, locale));
return siteListVO;
}
private boolean isSearchActivated(String searchFor) {
//return !CollectionUtils.isEmpty(searchFor);
return !StringUtils.isEmpty(searchFor);
}
}
AngularJs code :
$scope.getContactList = function () {
var url = $scope.url;
$scope.lastAction = 'list';
$scope.startDialogAjaxRequest();
var config = {params: {page: $scope.pageToGet}};
$http.get(url, config)
.success(function (data) {
// console.log(data);
console.debug(data);
$scope.finishAjaxCallOnSuccess(data, null, false);
})
.error(function () {
$scope.state = 'error';
$scope.displayCreateContactButton = false;
});
}
$scope.populateTable = function (data) {
if (data.pagesCount > 0) {
$scope.state = 'list';
$scope.page = {source: data.sites, currentPage: $scope.pageToGet, pagesCount: data.pagesCount, totalContacts : data.totalContacts};
if($scope.page.pagesCount <= $scope.page.currentPage){
$scope.pageToGet = $scope.page.pagesCount - 1;
$scope.page.currentPage = $scope.page.pagesCount - 1;
}
$scope.displayCreateContactButton = true;
$scope.displaySearchButton = true;
} else {
$scope.state = 'noresult';
$scope.displayCreateContactButton = true;
if(!$scope.searchFor){
$scope.displaySearchButton = false;
}
}
if (data.actionMessage || data.searchMessage) {
$scope.displayMessageToUser = $scope.lastAction != 'search';
$scope.page.actionMessage = data.actionMessage;
$scope.page.searchMessage = data.searchMessage;
} else {
$scope.displayMessageToUser = false;
}
}
In spring mvc XML i have :
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="filter.HibernateAwareObjectMapper" />
</property>
</bean>
<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
</list>
</property>
</bean>
code of class HibernateAwareObjectMapper :
public class HibernateAwareObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public HibernateAwareObjectMapper() {
registerModule(new Hibernate4Module());
}
}
Web XML i have this filter :
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
;
i'm using the following dependency :
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.4.0</version>
</dependency>
is there a way to Load collection by Open Session ?
thank you in advance for you replay
I solve that by adding this annotation #LazyCollection(LazyCollectionOption.FALSE)
//#JsonSerialize(using = CustomListSerializer.class)
//#JsonView(Views.Internal.class)
//#JsonIgnore
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
public Set<Sites> getSitees() {
return sitees;
}
One possibility is to add the OpenEntityManagerInViewFilter which keeps the session open in the view. Example:
<filter>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Spring OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Apache databaseConfiguration doesn't work with MapConfiguration

I am trying to combine both mapConfiguration(file with properties) with databaseConfiguration. However only the file properties can be looked up but not the database properties. What am I doing wrong?
public class MapConfigProvider extends ConfigurationProvider {
Map properties = null;
public MapConfigProvider(Map inProps) {
super(MapConfiguration.class);
properties = inProps;
}
#Override
public AbstractConfiguration getConfiguration(ConfigurationDeclaration decl) throws Exception {
MapConfiguration mapConfig = new MapConfiguration(properties);
return mapConfig;
}
}
public class DatabaseConfigurationProvider extends ConfigurationProvider {
private DatabaseConfigDef dbConfigDef;
public DatabaseConfigurationProvider(DatabaseConfigDef databaseConfigDef)
{
super(DatabaseConfiguration.class);
this.dbConfigDef = databaseConfigDef;
}
public DatabaseConfigurationProvider() {
super();
}
#Override
public AbstractConfiguration getConfiguration(ConfigurationDeclaration decl) throws Exception {
DataSource ds = DataSourceFactory.getInstance().getDataSource(DATASOURCE.SEMS);
return new DatabaseConfiguration(ds, "CSW_TABLE", "PROP_COL", "VALUE_COL"); //String values sought from databaseConfigDef
}
}
private static final Map<String, Object> propertiesMap= new HashMap<String, Object>();
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(propFileUrl);
builder.addConfigurationProvider("filebased", new MapConfigProvider(propertiesMap));
builder.addConfigurationProvider("ctkdatabase", new DatabaseConfigurationProvider(databaseConfigDef));
CombinedConfiguration combinedConfig = builder.getConfiguration(true);
String prop1 = combinedConfig.getString("WCM_WEBSERVICE_PORT"); //From DB, gets null
String prop2 = combinedConfig.getString("temp"); //From file, fetches good
LOGGER.info(prop1 + prop2);
What am I doing wrong? Please help.
1) In the propFileUrl: mainconfig.xml, I have to specify a
<configuration>
<header>
<result delimiterParsingDisabled="true" forceReloadCheck="false">
<nodeCombiner config-class="org.apache.commons.configuration.tree.OverrideCombiner" />
<expressionEngine config-class="org.apache.commons.configuration.tree.xpath.XPathExpressionEngine" />
</result>
</header>
<system /> <!-- load system properties into config -->
<properties fileName="common/datasources.properties" />
<properties fileName="application.properties" />
<ctkdatabase jndi="jdbc/shaDs" table="TABLE_APPL_PROPERTIES" keyColumn="PROP_NAME" valueColumn="PROP_VALUE"/>
<!-- load host's config file -->
<xml fileName="app/cluster/datasources.xml" />
</configuration>
2) Create a new getter/setter to "jndi" variable in DatabaseConfigDef class.
3) And change getConfiguration() to:
#Override
public AbstractConfiguration getConfiguration(ConfigurationDeclaration decl) throws Exception {
DatabaseConfigDef def = (DatabaseConfigDef)createBean(DatabaseConfigDef.class, decl, null);
Context env = (Context) new InitialContext().lookup("java:comp/env");
DataSource ds = (DataSource)env.lookup((def.getJndi()));
return new DatabaseConfiguration(ds, def.getTable(), def.getKeyColumn(), def.getValueColumn());
}

Failed to create an instance of Service via deferred binding

I have been trying to build a GWT / Google App Engine web app using the mvp4g framework.
I keep getting an error about Failing to create an instance of my Service via deferred binding.
My Acebankroll.gwt.xml file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='acebankroll'>
<inherits name='com.google.gwt.user.User'/>
<inherits name="com.google.gwt.i18n.I18N"/>
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<inherits name='com.mvp4g.Mvp4gModule'/>
<entry-point class='com.softamo.acebankroll.client.AceBankroll'/>
<source path='client'/>
</module>
My Entry Module looks like:
public class AceBankroll implements EntryPoint {
public void onModuleLoad() {
Mvp4gModule module = (Mvp4gModule)GWT.create( Mvp4gModule.class );
module.createAndStartModule();
RootPanel.get().add((Widget)module.getStartView());
}
}
Error Trace
I post the complete error trace as an answer.
FAQ and Trials
I have read that the next list of common mistakes may cause this error:
The ServiceAsync interfaces have methods with return values. This is wrong, all methods need to return void.
The Service interfaces don't extend the RemoteService interface.
The methods in the ServiceAsync interfaces miss the final argument of AsyncCallback.
The methods on the two interfaced, ExampleService and ExampleServiceAsync, don't match up exactly (other than the return value and AsyncCallback argument)
I have checked all the above conditions and did not find the problem.
How do you insert your services in the presenters?
Here is a snippet illustrating how I do inject the service in my presenter classes.
protected MainServiceAsync service = null;
#InjectService
public void setService( MainServiceAsync service ) {
this.service = service;
}
Do you have the required libraries?
Yes, I have commons-configuration-1.6.jar, commons-lang-2.4.jar and mvp4g-1.1.0.jar in my lib directory.
Does your project compiles?
Yes, it does compile. I use Eclipse with GWT/Google App Engine plugin. Next I post my .classpath
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" output="test-classes" path="test"/>
<classpathentry kind="con" path="com.google.appengine.eclipse.core.GAE_CONTAINER"/>
<classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/commons-configuration-1.6.jar"/>
<classpathentry kind="lib" path="lib/commons-lang-2.4.jar"/>
<classpathentry kind="lib" path="lib/mvp4g-1.1.0.jar"/>
<classpathentry kind="lib" path="test/lib/emma.jar"/>
<classpathentry kind="lib" path="test/lib/junit-4.5.jar"/>
<classpathentry kind="lib" path="C:/Users/sdelamo/Programms/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412/appengine-java-sdk-1.3.1/lib/testing/appengine-testing.jar"/>
<classpathentry kind="lib" path="C:/Users/sdelamo/Programms/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412/appengine-java-sdk-1.3.1/lib/impl/appengine-api.jar"/>
<classpathentry kind="lib" path="C:/Users/sdelamo/Programms/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412/appengine-java-sdk-1.3.1/lib/impl/appengine-api-labs.jar"/>
<classpathentry kind="lib" path="C:/Users/sdelamo/Programms/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412/appengine-java-sdk-1.3.1/lib/impl/appengine-api-stubs.jar"/>
<classpathentry kind="lib" path="C:/Users/sdelamo/Programms/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.1_1.3.1.v201002101412/appengine-java-sdk-1.3.1/lib/impl/appengine-local-runtime.jar"/>
<classpathentry kind="output" path="war/WEB-INF/classes"/>
</classpath>
Are your Bean Serializable?
Yes, they are serializable. They implements the next interface:
public interface BasicBean extends Serializable {
public String getId();
public void copy(BasicBean ob);
}
They all have an empty argument constructor. Some of them have two constructors. One without arguments and one with arguments.
Some of them implement this interface
public interface NameObject extends BasicBean, BaseOwnedObject, Comparable<NameObject> {
public String getName();
public void setName(String name);
public abstract int compareTo(NameObject ob);
}
Can the Comparable cause problems?
How does your service code looks like?
I post my service code:
MainService
#RemoteServiceRelativePath( "main" )
public interface MainService extends RemoteService {
public List<UserBean> getUsers();
public void deleteUser(UserBean user);
public void createUser(UserBean user);
public void updateUser( UserBean user );
public String authenticate(String username, String password);
public boolean isSessionIdStillLegal(String sessionId);
public void signOut();
public boolean userAlreadyExists(String email);
public UserBean getByEmail(String email);
public void confirmUser(String email);
public UserBean getUserById(String id);
}
MainServiceAsync
public interface MainServiceAsync {
public void getUsers(AsyncCallback<List<UserBean>> callback);
public void deleteUser(UserBean user, AsyncCallback<Void> callback);
public void createUser(UserBean user, AsyncCallback<Void> callback);
public void updateUser( UserBean user, AsyncCallback<Void> callback);
public void authenticate(String username, String password, AsyncCallback<String> callback);
public void isSessionIdStillLegal(String sessionId, AsyncCallback<Boolean> callback);
public void signOut(AsyncCallback<Void> callback);
public void userAlreadyExists(String email, AsyncCallback<Boolean> callback);
public void getByEmail(String email, AsyncCallback<UserBean> callback );
public void confirmUser(String email, AsyncCallback<Void> callback );
public void getUserById(String id, AsyncCallback<UserBean> callback);
}
Basic Bean
import java.io.Serializable;
public interface BasicBean extends Serializable {
public String getId();
public void copy(BasicBean ob);
}
User Bean
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class UserBean implements BasicBean {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
protected Long ident;
#Persistent
private String name = null;
#Persistent
private String email = null;
#Persistent
private boolean confirmed = false;
#Persistent
private String password = null;
public UserBean() { }
public String getId() {
if( ident == null ) return null;
return ident.toString();
}
public void setId(String id) {
this.ident = Long.parseLong(id);
}
public String getEmail( ) { return email; }
public void setEmail(String email) { this. email = email; }
public String getName() { return name; }
public void setName(String name) { this. name = name; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password;}
public boolean isConfirmed() { return confirmed;}
public void setConfirmed(boolean confirmed) {this.confirmed = confirmed;}
public void copy(BasicBean ob) {
UserBean user = (UserBean) ob;
this.name = user.name;
this.email = user.email;
this.password = user.password;
}
}
Next I post an extract of web.xml
Note. I have 7 other services. I am using the module functionality of MVP4G. I have other servlets defined for each module in web.xml
<servlet>
<servlet-name>mainServlet</servlet-name>
<servlet-class>com.softamo.acebankroll.server.MainServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>mainServlet</servlet-name>
<url-pattern>/acebankroll/main</url-pattern>
</servlet-mapping>
Server
BaseServiceImpl
public abstract class BaseServiceImpl extends RemoteServiceServlet {
protected Map users = new HashMap();
protected static final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
protected static final Logger log = Logger.getLogger(BaseServiceImpl.class.getName());
protected String getSessionId() {
return getThreadLocalRequest().getSession().getId();
}
protected String getCurrentUserId() {
String id = getSessionId();
UserBean user = (UserBean) users.get(id);
if(user!=null)
return user.getId();
return null;
}
protected void saveBaseObject(BasicBean ob) {
PersistenceManager pm = JdoUtil.getPm();
String sessionId = getSessionId();
UserBean user = (UserBean) users.get(sessionId);
if(user!=null) {
String user_id = user.getId();
((BaseOwnedObject)ob).setUserId(user_id);
pm.makePersistent(ob);
}
}
protected void deleteBaseObject(Class classname, String id) {
PersistenceManager pm = JdoUtil.getPm();
pm.deletePersistent( pm.getObjectById(classname, Long.parseLong(id) ));
}
protected List getAll(Class class_name) {
PersistenceManager pm = JdoUtil.getPm();
pm.setDetachAllOnCommit(true);
Query q = pm.newQuery(class_name);
if(q==null)
return new ArrayList<BasicBean>();
q.setFilter("userId == userIdParam");
q.declareParameters("String userIdParam");
String userId = getCurrentUserId();
return (List) q.execute(userId);
}
public boolean isSessionIdStillLegal(String sessionId) {
return (users.containsKey(sessionId))? true : false;
}
public void signOut() {
String id = getSessionId();
synchronized(this) {
users.remove(id);
}
}
public BasicBean getObjectById(Class classname, String id) {
BasicBean result = null;
PersistenceManager pm = JdoUtil.getPm();
pm.setDetachAllOnCommit(true);
result = pm.getObjectById(classname, Long.parseLong(id) );
return result;
}
}
MainServiceImpl
public class MainServiceImpl extends BaseServiceImpl implements MainService {
public MainServiceImpl() {}
public String authenticate(String username, String password) {
PersistenceManager pm = JdoUtil.getPm();
UserBean user = getByEmail(username);
if(user==null || !user.isConfirmed())
return null;
String hashFromDB = user.getPassword();
boolean valid = BCrypt.checkpw(password, hashFromDB);
if(valid) {
String id = getSessionId();
synchronized( this ) {
users.put(id, user) ;
}
return id;
}
return null;
}
public void deleteUser(UserBean user) {
deleteBaseObject(UserBean.class, user.getId());
}
public List<UserBean> getUsers() {
PersistenceManager pm = JdoUtil.getPm();
pm.setDetachAllOnCommit(true);
Query q = pm.newQuery(UserBean.class);
if(q==null)
return new ArrayList<UserBean>();
return (List) q.execute();
}
public boolean userAlreadyExists(String email) {
return (getByEmail(email)!=null) ? true : false;
}
public void updateUser(UserBean object) {
saveBaseObject(object);
}
public void confirmUser(String email) {
PersistenceManager pm = JdoUtil.getPm();
UserBean user = getByEmail(email);
if(user!=null) {
user.setConfirmed(true);
pm.makePersistent(user);
}
}
public void createUser(UserBean user) {
PersistenceManager pm = JdoUtil.getPm();
String sessionId = getSessionId();
// Only store it if it does not exists
if( (getByEmail(user.getEmail()))==null) {
String hash = BCrypt.hashpw(user.getPassword(), BCrypt.gensalt());
user.setPassword(hash);
pm.makePersistent(user);
synchronized( this ) {
users.put(sessionId, user);
}
}
}
public UserBean getByEmail(String email) {
return new MyAccountServiceImpl().getByEmail(email);
}
public UserBean getUserById(String id) {
return new MyAccountServiceImpl().getUserById(id);
}
}
SOLUTION
Apparently the Google App Engine Annotations in my Bean classes were causing the problem. Removing the annotation from the client side code solved the issue. What I do know if I have the classes with the JDO notation in the server side. That it is to say the beans are plain data transfere object which get cloned into object with JDO annotations in the server side.
I am literally stacked. I do not know what to try. Any help is really appreciated!
If your service methods contains POJO's they can cause you problems, they must have a zero argument constructor or no constructor defined. Also they must implement either IsSerializable or Serializable.
You can trie to create the service manually with:
MainServiceAsync service = GWT.create(MainService.class);
And maybe post the MainService classes.
Edited:
This is an output from the treelogger with a deferred binding failing, and it is outputed into the console when you do a gwt compile. You can also see this output in the devmode console if you run in hosted mode. Always check the first error, because the others are most of the time caused by the first error.
Compiling module se.pathed.defa.DefaultGwtProject
Scanning for additional dependencies: file:/C:/Users/Patrik/workspace/skola-workspace/DefaultGwtProject/src/se/pathed/defa/client/DefaultGwtProject.java
Computing all possible rebind results for 'se.pathed.defa.client.GreetingService'
Rebinding se.pathed.defa.client.GreetingService
Invoking com.google.gwt.dev.javac.StandardGeneratorContext#16c6a55
Generating client proxy for remote service interface 'se.pathed.defa.client.GreetingService'
[ERROR] se.pathed.defa.shared.UserBean is not default instantiable (it must have a zero-argument constructor or no constructors at all) and has no custom serializer. (reached via se.pathed.defa.shared.UserBean)
[ERROR] se.pathed.defa.shared.UserBean has no available instantiable subtypes. (reached via se.pathed.defa.shared.UserBean)
[ERROR] subtype se.pathed.defa.shared.UserBean is not default instantiable (it must have a zero-argument constructor or no constructors at all) and has no custom serializer. (reached via se.pathed.defa.shared.UserBean)
[ERROR] Errors in 'file:/C:/Users/Patrik/workspace/skola-workspace/DefaultGwtProject/src/se/pathed/defa/client/DefaultGwtProject.java'
[ERROR] Line 37: Failed to resolve 'se.pathed.defa.client.GreetingService' via deferred binding
Scanning for additional dependencies: jar:file:/C:/eclipse/plugins/com.google.gwt.eclipse.sdkbundle.2.0.3_2.0.3.v201002191036/gwt-2.0.3/gwt-user.jar!/com/google/gwt/core/client/impl/SchedulerImpl.java
[WARN] The following resources will not be created because they were never committed (did you forget to call commit()?)
[WARN] C:\Users\Patrik\AppData\Local\Temp\gwtc301646733929273376.tmp\se.pathed.defa.DefaultGwtProject\compiler\se.pathed.defa.client.GreetingService.rpc.log
[WARN] For the following type(s), generated source was never committed (did you forget to call commit()?)
[WARN] se.pathed.defa.client.GreetingService_Proxy
[ERROR] Cannot proceed due to previous errors
This can happen if anything that is in your client package has an import that is not whitelisted. For example i hit this because my autoimport imported an apache commons lang class into my client code.
One would have to look at their imports to make sure nothing odd is in the client code.
GWT projects are structured like:
com.app.client
com.app.server
you can't having anything that not GWT compatible in client.

Resources