Skip to content

tutorial crud

Abhay Chandel edited this page Dec 11, 2018 · 12 revisions

Creating a CRUD functionality for an entity

In this tutorial we are going to create an entity for the application and provide services for Create, Read, Update and Delete instances of that entity.

It is important to mention devonfw packaging convention. devonfw uses a strict packaging convention to map technical layers and business components to the code. devonfw uses the following Java-Package schema:

<basepackage>.<component>.<layer>.<scope>[.<detail>]*

In our example application we find the different classes in this packages:

  • Entity and DAO: com.devonfw.application.mtsj.ordermanagement.dataaccess.api[.<detail>]

  • Logic: com.devonfw.application.mtsj.ordermanagement.logic[.<detail>]

  • Services: com.devonfw.application.mtsj.ordermanagement.service[.<detail>]

For more information you can consult packaging devonfw documentation

Persitence provider configuration

Create the JPA entity

We are going to create a Order entity. First, we are going to create the Order entity interface. This will be reused between all the objects involved with order on the different layers.

import com.devonfw.application.mtsj.general.common.api.ApplicationEntity;

public interface Order extends ApplicationEntity {

  public Long getBookingId();

  public void setBookingId(Long bookingId);

  public Long getInvitedGuestId();

  public void setInvitedGuestId(Long invitedGuestId);

  public Long getHostId();

  public void setHostId(Long hostId);

}

As you can see, Table should extend ApplicationEntity class. This class provides the neccesary methods for a mutable entity (ID getter and setter basically).

Finally, we should create the entity implementation:

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.devonfw.application.mtsj.bookingmanagement.dataaccess.api.BookingEntity;
import com.devonfw.application.mtsj.bookingmanagement.dataaccess.api.InvitedGuestEntity;
import com.devonfw.application.mtsj.general.dataaccess.api.ApplicationPersistenceEntity;
//import io.oasp.application.mtsj.ordermanagement.common.api.Order;

/**
 * The {@link com.devonfw.application.mtsj.general.dataaccess.api.ApplicationPersistenceEntity persistent entity} for
 * {@link Order}.
 */
@Entity
@Table(name = "Orders")
public class OrderEntity extends ApplicationPersistenceEntity implements Order {

  private static final long serialVersionUID = 1L;

  private BookingEntity booking;

  private InvitedGuestEntity invitedGuest;

  private BookingEntity host;

  private List<OrderLineEntity> orderLines;

  /**
   * @return booking
   */
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "idBooking")
  public BookingEntity getBooking() {

    return this.booking;
  }

  /**
   * @param booking new value of {@link #getbooking}.
   */
  public void setBooking(BookingEntity booking) {

    this.booking = booking;
  }

  /**
   * @return invitedGuest
   */
  @OneToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "idInvitedGuest")
  public InvitedGuestEntity getInvitedGuest() {

    return this.invitedGuest;
  }

  /**
   * @param invitedGuest new value of {@link #getinvitedGuest}.
   */
  public void setInvitedGuest(InvitedGuestEntity invitedGuest) {

    this.invitedGuest = invitedGuest;
  }

  /**
   * @return orderLines
   */
  @OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
  public List<OrderLineEntity> getOrderLines() {

    return this.orderLines;
  }

  /**
   * @param orderLines new value of {@link #getorderLines}.
   */
  public void setOrderLines(List<OrderLineEntity> orderLines) {

    this.orderLines = orderLines;
  }

  @Override
  @Transient
  public Long getBookingId() {

    if (this.booking == null) {
      return null;
    }
    return this.booking.getId();
  }

  @Override
  public void setBookingId(Long bookingId) {

    if (bookingId == null) {
      this.booking = null;
    } else {
      BookingEntity bookingEntity = new BookingEntity();
      bookingEntity.setId(bookingId);
      this.booking = bookingEntity;
    }
  }

  @Override
  @Transient
  public Long getInvitedGuestId() {

    if (this.invitedGuest == null) {
      return null;
    }
    return this.invitedGuest.getId();
  }

  @Override
  public void setInvitedGuestId(Long invitedGuestId) {

    if (invitedGuestId == null) {
      this.invitedGuest = null;
    } else {
      InvitedGuestEntity invitedGuestEntity = new InvitedGuestEntity();
      invitedGuestEntity.setId(invitedGuestId);
      this.invitedGuest = invitedGuestEntity;
    }
  }

  /**
   * @return host
   */
  @OneToOne
  @JoinColumn(name = "idHost")
  public BookingEntity getHost() {

    return this.host;
  }

  /**
   * @param host new value of {@link #gethost}.
   */
  public void setHost(BookingEntity host) {

    this.host = host;
  }

  @Override
  @Transient
  public Long getHostId() {

    if (this.host == null) {
      return null;
    }
    return this.host.getId();
  }

  @Override
  public void setHostId(Long hostId) {

    if (hostId == null) {
      this.host = null;
    } else {
      BookingEntity bookingEntity = new BookingEntity();
      bookingEntity.setId(hostId);
      this.host = bookingEntity;
    }
  }

}

Validation

You can read more about devonfw validation in devonfw validation

For example, we are going to add a validation in TableEntity to validate number property to allow only values greater than 0.

  @Min(value = 1, message = "Assistants must be greater than 0")
  @Digits(integer = 2, fraction = 0)
  private Integer assistants;

Creating persistence layer

Data Acccess Objects (DAOs) are part of the persistence layer. They are responsible for a specific entity and should be named <entity>Dao[Impl]. The DAO offers the so called CRUD-functionalities (create, retrieve, update, delete) for the corresponding entity. Additionally a DAO may offer advanced operations such as search or locking methods.

For each DAO there is an interface named <entity>Dao that defines the API. For CRUD support and common naming methods we derive it from the interface com.devonfw.application.mtsj.general.dataaccess.api.dao.

OrderDao.java
// import io.oasp.application.mtsj.general.dataaccess.api.dao.ApplicationDao;
import com.devonfw.application.mtsj.ordermanagement.dataaccess.api.OrderEntity;
import com.devonfw.application.mtsj.ordermanagement.logic.api.to.OrderSearchCriteriaTo;
import com.devonfw.module.jpa.common.api.to.PaginatedListTo;

/**
 * Data access interface for Order entities
 */
public interface OrderDao extends ApplicationDao<OrderEntity> {

  /**
   * Finds the {@link OrderEntity orders} matching the given {@link OrderSearchCriteriaTo}.
   *
   * @param criteria is the {@link OrderSearchCriteriaTo}.
   * @return the {@link PaginatedListTo} with the matching {@link OrderEntity} objects.
   */
  PaginatedListTo<OrderEntity> findOrders(OrderSearchCriteriaTo criteria);

}

Implementing a DAO is quite simple. We should create a class named <entity>DaoImpl that extends ApplicationDaoImpl class and implements our DAO interface.

This is the DAO implementation for our table sample:

OrderDaoImpl.java
import java.util.List;

import javax.inject.Named;

import com.mysema.query.alias.Alias;
import com.mysema.query.jpa.impl.JPAQuery;
import com.mysema.query.types.path.EntityPathBase;

import com.cap.jumpthequeue.general.dataaccess.base.dao.ApplicationDaoImpl;
import com.devonfw.application.mtsj.ordermanagement.dataaccess.api.OrderEntity;
// import io.oasp.application.mtsj.ordermanagement.dataaccess.api.dao.OrderDao;
import com.devonfw.application.mtsj.ordermanagement.logic.api.to.OrderSearchCriteriaTo;
// import io.oasp.module.jpa.common.api.to.OrderByTo;
// import io.oasp.module.jpa.common.api.to.OrderDirection;
import com.devonfw.module.jpa.common.api.to.PaginatedListTo;

/**
 * This is the implementation of {@link OrderDao}.
 */
@Named
public class OrderDaoImpl extends ApplicationDaoImpl<OrderEntity> implements OrderDao {

  /**
   * The constructor.
   */
  public OrderDaoImpl() {

    super();
  }

  @Override
  public Class<OrderEntity> getEntityClass() {

    return OrderEntity.class;
  }

  @Override
  public PaginatedListTo<OrderEntity> findOrders(OrderSearchCriteriaTo criteria) {

    OrderEntity order = Alias.alias(OrderEntity.class);
    EntityPathBase<OrderEntity> alias = Alias.$(order);
    JPAQuery query = new JPAQuery(getEntityManager()).from(alias);

    Long booking = criteria.getBookingId();
    if (booking != null && order.getBooking() != null) {
      query.where(Alias.$(order.getBooking().getId()).eq(booking));
    }
    Long invitedGuest = criteria.getInvitedGuestId();
    if (invitedGuest != null && order.getInvitedGuest() != null) {
      query.where(Alias.$(order.getInvitedGuest().getId()).eq(invitedGuest));
    }
    String hostToken = criteria.getHostToken();
    if (hostToken != null && order.getHost() != null) {
      query.where(Alias.$(order.getBooking().getBookingToken()).toLowerCase().eq(hostToken.toLowerCase()));
    }

    String email = criteria.getEmail();
    if (email != null) {
      query.where(Alias.$(order.getBooking().getEmail()).toLowerCase().eq(email.toLowerCase()));
    }

    String bookingToken = criteria.getBookingToken();
    if (bookingToken != null) {
      query.where(Alias.$(order.getBooking().getBookingToken()).toLowerCase().eq(bookingToken.toLowerCase()));
    }

    addOrderBy(query, alias, order, criteria.getSort());
    return findPaginated(criteria, query, alias);
  }

  private void addOrderBy(JPAQuery query, EntityPathBase<OrderEntity> alias, OrderEntity order, List<OrderByTo> sort) {

    if (sort != null && !sort.isEmpty()) {
      for (OrderByTo orderEntry : sort) {
        if ("idBooking".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getBookingId()).asc());
          } else {
            query.orderBy(Alias.$(order.getBookingId()).desc());
          }
        } else if ("idInvitedGuest".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getInvitedGuestId()).asc());
          } else {
            query.orderBy(Alias.$(order.getInvitedGuestId()).desc());
          }
        } else if ("hostToken".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getBooking().getBookingToken()).toLowerCase().asc());
          } else {
            query.orderBy(Alias.$(order.getBooking().getBookingToken()).toLowerCase().desc());
          }
        } else if ("bookingToken".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getBooking().getBookingToken()).toLowerCase().asc());
          } else {
            query.orderBy(Alias.$(order.getBooking().getBookingToken()).toLowerCase().desc());
          }
        } else if ("email".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getBooking().getEmail()).toLowerCase().asc());
          } else {
            query.orderBy(Alias.$(order.getBooking().getEmail()).toLowerCase().desc());
          }
        } else if ("bookingDate".equals(orderEntry.getName())) {
          if (OrderDirection.ASC.equals(orderEntry.getDirection())) {
            query.orderBy(Alias.$(order.getBooking().getBookingDate()).asc());
          } else {
            query.orderBy(Alias.$(order.getBooking().getBookingDate()).desc());
          }
        }
      }
    }
  }

}

As you can see ApplicationMasterDataDaoImpl already implements the CRUD operations so you only have to implement the additional methods that you have declared in your <entity>Dao interface.

Defining querys

devonfw advises to specify all queries in one mapping file called NamedQueries.xml. So we are going to create a query to get free tables that we have used in TableDaoImpl.

src/main/resources/config/app/dataaccess/NamedQueries.xml
<!--?xml version="1.0" encoding="UTF-8"?-->
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">

  <named-query name="get.free.tables">
    <query><!--[CDATA[SELECT t FROM Table t WHERE t.state = com.devonfw.gastronomy.restaurant.common.datatype.TableState.FREE]]--></query>
  </named-query>

</entity-mappings>

To avoid redundant occurrences of the query name we define the constants for each named query:

NamedQueries.java
/**
 * Constants of the named queries defined in ``NamedQueries.xml``.
 *
 */
public abstract class NamedQueries {

  // put your query names from NamedQueries.xml as constants here
  /** @see io.oasp.gastronomy.restaurant.tablemanagement.dataaccess.impl.dao.TableDaoImpl#getFreeTables() */
  public static final String GET_FREE_TABLES = "get.free.tables";

}

Note that changing the name of the java constant can be done easily with refactoring. Further you can trace where the query is used by searching the references of the constant.

Expose logic as services

The logic layer is for internal use of an application. In order to access the functionality of the logic layer from other applications it should be exposed with a bridge layer called the service layer.

This layer should be in charge of converting between Java objects to its serialized form and back. It also provide the means to publish to an endpoint and securize the access to certain users. Last but not less important it is responsible to wrap any error coming from the logic layer to a format that would be understood by the client of the service.

In devonfw, we propose to divide the CRUD logic into different files to sepparate responsability:

  • An interface and an implementing class for CRUD read only methods, UCFind[XXX]. E.g. UCFindTable.

  • An interface and an implementing class fro CRUD write methods, UCManage[XXX]. E.g. UCManageTable.

UCFindTable.java
import com.devonfw.application.mtsj.bookingmanagement.logic.api.to.TableEto;

import java.util.List;

/**
 * Interface of UcFindTable to centralize documentation and signatures of methods.
 *
 */
public interface UcFindTable {

  /**
   * Returns a restaurant table by its id 'id'.
   *
   * @param id The id 'id' of the restaurant table.
   * @return The restaurant {@link TableEto} with id 'id'
   */
  TableEto findTable(Long id);

  /**
   * Returns a list of all existing restaurant tables.
   *
   * @return {@link List} of all existing restaurant {@link TableEto}s
   */
  List<tableeto> findAllTables();

 /**
   * Returns a list of all existing free restaurant tables.
   *
   * @return {@link List} of all existing free restaurant {@link TableEto}s
   */
  List<tableeto> findFreeTables();

}
UCFindTableImpl.java
import com.devonfw.application.mtsj.general.common.api.constants.PermissionConstants;
import com.devonfw.application.mtsj.general.logic.api.UseCase;
import com.devonfw.application.mtsj.general.dataaccess.api.TableEntity;
import com.devonfw.application.mtsj.general.logic.api.to.TableEto;
import com.devonfw.application.mtsj.general.logic.api.usecase.UcFindTable;
import com.devonfw.application.mtsj.general.logic.base.usecase.AbstractTableUc;

import java.util.List;

import javax.annotation.security.RolesAllowed;
import javax.inject.Named;

/**
 * Implementation of {@link UcFindTable}.
 *
 */
@Named
@UseCase
public class UcFindTableImpl extends AbstractTableUc implements UcFindTable {

  /**
   * {@inheritDoc}
   */
  @Override
  @RolesAllowed(PermissionConstants.FIND_TABLE)
  public TableEto findTable(Long id) {

    return getBeanMapper().map(getTableDao().findOne(id), TableEto.class);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  @RolesAllowed(PermissionConstants.FIND_TABLE)
  public List<tableeto> findAllTables() {

    List<tableentity> tables = getTableDao().findAll();
    return getBeanMapper().mapList(tables, TableEto.class);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  @RolesAllowed(PermissionConstants.FIND_TABLE)
  public List<tableeto> findFreeTables() {

    List<tableentity> tables = getTableDao().getFreeTables();
    return getBeanMapper().mapList(tables, TableEto.class);
  }

}
UCManageTable.java
import com.devonfw.application.mtsj.general.logic.api.to.TableEto;

import javax.validation.Valid;

/**
 * Interface of UcManageTable to centralize documentation and signatures of methods.
 *
 */
public interface UcManageTable {

  /**
   * Deletes a restaurant table from the database by its id 'id'.
   *
   * @param tableId Id of the restaurant table to delete
   */
  void deleteTable(Long tableId);

  /**
   * Creates a new restaurant table and store it in the database.
   *
   * @param table the {@link TableEto} to create.
   * @return the new {@link TableEto} that has been saved with ID and version.
   */
  TableEto saveTable(@Valid TableEto table);

}
UCManageTableImpl.java
import com.devonfw.application.mtsj.general.common.api.constants.PermissionConstants;
import com.devonfw.application.mtsj.general.common.api.exception.IllegalEntityStateException;
import com.devonfw.application.mtsj.general.logic.api.UseCase;
import com.devonfw.gastronomy.restaurant.common.datatype.TableState;
import com.devonfw.application.mtsj.bookingmanagement.dataaccess.api.TableEntity;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.to.TableEto;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.usecase.UcManageTable;
import com.devonfw.application.mtsj.bookingmanagement.logic.base.usecase.AbstractTableUc;

import java.util.Objects;

import javax.annotation.security.RolesAllowed;
import javax.inject.Named;
import javax.validation.Valid;

import org.springframework.validation.annotation.Validated;

/**
 * Implementation of {@link UcManageTable}.
 *
 */
@Named
@UseCase
@Validated
public class UcManageTableImpl extends AbstractTableUc implements UcManageTable {

  /**
   * {@inheritDoc}
   */
  @Override
  @RolesAllowed(PermissionConstants.DELETE_TABLE)
  public void deleteTable(Long tableId) {

    TableEntity table = getTableDao().find(tableId);

    if (!table.getState().isFree()) {
      throw new IllegalEntityStateException(table, table.getState());
    }

    getTableDao().delete(table);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  @RolesAllowed(PermissionConstants.SAVE_TABLE)
  public TableEto saveTable(@Valid TableEto table) {

    Objects.requireNonNull(table, "table");

    TableEntity tableEntity = getBeanMapper().map(table, TableEntity.class);
    // initialize
    if (tableEntity.getState() == null) {
      tableEntity.setState(TableState.FREE);
    }

    getTableDao().save(tableEntity);
    return getBeanMapper().map(tableEntity, TableEto.class);
  }

}

As you can see, implementation classes extend AbstractTableUC class. This class provides the DAO class injection.

AbstractTableUC.java
import com.devonfw.application.mtsj.general.logic.base.AbstractUc;
import com.devonfw.application.mtsj.bookingmanagement.logic.impl.BookingmanagementImpl;

import javax.inject.Inject;

/**
 *
 */
public abstract class AbstractTableUc extends AbstractUc {

  /** @see #getTableDao() */
  private TableDao tableDao;

  /**
   * @return the {@link TableDao} instance.
   */
  public TableDao getTableDao() {

    return this.tableDao;
  }

  /**
   * @param tableDao the {@link TableDao} to {@link Inject}.
   */
  @Inject
  public void setTableDao(TableDao tableDao) {

    this.tableDao = tableDao;
  }

}

Finally, we are going to create an interface and the implementating class that joins both UC classes. devonfw naming convention for this classes are: [XXX]management and [XXX]managementImpl.

Tablemanagement.java
import com.devonfw.application.mtsj.general.logic.api.usecase.UcFindTable;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.usecase.UcManageTable;

/**
 * Interface for TableManagement component.
 *
 */
public interface Tablemanagement extends UcFindTable, UcManageTable {

}
TablemanagementImpl.java
import com.devonfw.application.mtsj.general.common.base.AbstractBeanMapperSupport;
import com.devonfw.application.mtsj.general.logic.api.UseCase;
// import io.oasp.gastronomy.restaurant.tablemanagement.logic.api.Tablemanagement;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.to.TableEto;
import com.devonfw.application.mtsj.general.logic.api.usecase.UcFindTable;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.usecase.UcManageTable;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;

/**
 * Implementation of {@link Tablemanagement}.
 *
 */
@Named
public class TablemanagementImpl extends AbstractBeanMapperSupport implements Tablemanagement {

  private UcFindTable ucFindTable;

  private UcManageTable ucManageTable;

  /**
   * The constructor.
   */
  public TablemanagementImpl() {

    super();
  }

  /**
   * Sets the field 'ucFindTable'.
   *
   * @param ucFindTable New value for ucFindTable
   */
  @Inject
  @UseCase
  public void setUcFindTable(UcFindTable ucFindTable) {

    this.ucFindTable = ucFindTable;
  }

  /**
   * Sets the field 'ucManageTable'.
   *
   * @param ucManageTable New value for ucManageTable
   */
  @Inject
  @UseCase
  public void setUcManageTable(UcManageTable ucManageTable) {

    this.ucManageTable = ucManageTable;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public TableEto findTable(Long id) {

    return this.ucFindTable.findTable(id);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List<tableeto> findAllTables() {

    return this.ucFindTable.findAllTables();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public List<tableeto> findAllTables() {

    return this.ucFindTable.findAllTables();
  }

  /**
   * {@inheritDoc}
   *
   */
  @Override
  public TableEto saveTable(TableEto table) {

    return this.ucManageTable.saveTable(table);
  }

  /**
   * {@inheritDoc}
   *
   */
  @Override
  public void deleteTable(Long id) {

    this.ucManageTable.deleteTable(id);
  }

}

This code shows that is merely a delegation for the injected UC and can be automatically generated with eclipse’s powerful refactoring capabilities.

Securing the application

devonfw focus on role-based authorization to cope with authorization for executing use case of an application. devonfw use the JSR250 annotations, mainly @RolesAllowed, as you have seen, for authorizing method calls against the permissions defined in the annotation body. So, finally, we have to create a class to declare the RollesAllowed annotation value as constants:

/**
 * Contains constants for the keys of all
 * {@link com.devonfw.module.security.common.api.accesscontrol.AccessControlPermission}s.
 *
 */
public abstract class PermissionConstants {

  /** {@link com.devonfw.module.security.common.api.accesscontrol.AccessControlPermission} to retrieve table. */
  public static final String FIND_TABLE = "FindTable";

  /** {@link com.devonfw.module.security.common.api.accesscontrol.AccessControlPermission} to save table. */
  public static final String SAVE_TABLE = "SaveTable";

  /** {@link com.devonfw.module.security.common.api.accesscontrol.AccessControlPermission} to remove table. */
  public static final String DELETE_TABLE = "DeleteTable";
}

Creating REST endpoints

Web applications need to get data from the server, so we have to expose the methods defined in the logic layer to this applications. We need a class that exposes methods as URLs to allow to the applications get the data. By convention, we call this class [XXX]managementRestServiceImpl where [XXX] will be the name of the entity.

This is an example of a REST API for our Table use case using JAX-RS. devonfw recommends to use CXF as the implementation for JAX-RS but other libraries following the standard will perform equally.

Also note that the implementation does not follow the canonical RESTFUL approach as devonfw proposes a more pragmatic way to use REST. Please refer to the Platform Guide service layer chapter for more information on the subject.

TablemanagementRestServiceImpl.java
import com.devonfw.application.mtsj.bookingmanagement.common.api.Table;
// import io.oasp.gastronomy.restaurant.tablemanagement.logic.api.Tablemanagement;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.to.TableEto;
import com.devonfw.application.mtsj.general.logic.api.usecase.UcFindTable;
import com.devonfw.application.mtsj.bookingmanagement.logic.api.usecase.UcManageTable;

import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import net.sf.mmm.util.exception.api.ObjectNotFoundUserException;

import org.springframework.transaction.annotation.Transactional;

/**
 * The service class for REST calls in order to execute the methods in {@link Tablemanagement}.
 */
@Path("/tablemanagement/v1")
@Named("TablemanagementRestService")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Transactional
public class TablemanagementRestServiceImpl {

  private Tablemanagement tableManagement;

  /**
   * This method sets the field <tt>tableManagement</tt>.
   *
   * @param tableManagement the new value of the field tableManagement
   */
  @Inject
  public void setTableManagement(Tablemanagement tableManagement) {

    this.tableManagement = tableManagement;
  }

  /**
   * Delegates to {@link UcFindTable#findTable}.
   *
   * @param id the ID of the {@link TableEto}
   * @return the {@link TableEto}
   */
  @GET
  @Path("/table/{id}/")
  public TableEto getTable(@PathParam("id") String id) {

    Long idAsLong;
    if (id == null) {
      throw new BadRequestException("missing id");
    }
    try {
      idAsLong = Long.parseLong(id);
    } catch (NumberFormatException e) {
      throw new BadRequestException("id is not a number");
    } catch (NotFoundException e) {
      throw new BadRequestException("table not found");
    }
    return this.tableManagement.findTable(idAsLong);
  }

  /**
   * Delegates to {@link UcFindTable#findAllTables}.
   *
   * @return list of all existing restaurant {@link TableEto}s
   */
  @GET
  @Path("/table/")
  public List<tableeto> getAllTables() {

    List<tableeto> allTables = this.tableManagement.findAllTables();
    return allTables;
  }

  /**
   * Delegates to {@link UcFindTable#findFreeTables}.
   *
   * @return list of all existing free {@link TableEto}s
   */
  @GET
  @Path("/freetables/")
  public List<tableeto> getFreeTables() {

    return this.tableManagement.findFreeTables();
  }

  /**
   * Delegates to {@link UcManageTable#saveTable}.
   *
   * @param table the {@link TableEto} to be created
   * @return the recently created {@link TableEto}
   */
  @POST
  @Path("/table/")
  public TableEto saveTable(TableEto table) {

    return this.tableManagement.saveTable(table);
  }

  /**
   * Delegates to {@link UcManageTable#deleteTable}.
   *
   * @param id ID of the {@link TableEto} to be deleted
   */
  @DELETE
  @Path("/table/{id}/")
  public void deleteTable(@PathParam("id") Long id) {

    this.tableManagement.deleteTable(id);
  }
}

Is important to mention:

  • We send and receive the information in JSON format.

  • We specify the version of the entire API or every method.

Finally, we need to add this implementation into JAX-RS server bean definition:

  <jaxrs:server id="CxfRestServices" address="/rest">
    <jaxrs:providers>
      <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider">
      <property name="mapper">
        <ref bean="JacksonObjectMapper">
      </ref></property>
      </bean>
      <ref bean="RestServiceExceptionFacade">
    </ref></jaxrs:providers>
    <jaxrs:servicebeans>
      <ref bean="TablemanagementRestService">
      <ref bean="SecurityRestService">
    </ref></ref></jaxrs:servicebeans>
  </jaxrs:server>

As you can see, we have defined the REST URLs for our Table user case. Now, for example, you can find all tables on this URL:

http://server:port/application-name/tablemanagement/v1/table/

DTO conversion

In the logic API, the methods of the classes should return Data Transfer Object (DTO) instead of entities. So, in devonfw we have a mechanism to convert the entities into DTOs.

This is an example of how to convert a entity into a DTO:

    // Conversion for lists
    getBeanMapper().mapList(tableList, TableDto.class);

    // Conversion for objects
    getBeanMapper().map(table, TableDto.class);

In the example, we use the function getBeanMapper(). This function provides us an API to convert entities into DTOs. In the logic layer, we only have to extend the class AbstractUc to access to this functionality.

Exceptions

User exceptions
Non controlled exceptions

Internationalization

Pagination

Sorting

/**
 * This enum identifies the entity, on which the sorting should be executed.
 *
 */
public enum TableSortByHitEntry {

  /**
   * Sort by id.
   */
  ID("id"),
  /**
   * Sort by number.
   */
  NUMBER("number"),
  /**
   * Sort by state.
   */
  STATE("state"),
  /**
   * Sort by waiterId.
   */
  WAITERID("waiterId");

  private final String sortByAttributeName;

  private TableSortByHitEntry(String sortByAttributeName) {

    this.sortByAttributeName = sortByAttributeName;
  }

  /**
   * @return sortByAttributeName
   */
  public String getSortByAttributeName() {

    return this.sortByAttributeName;
  }

  /**
   * This method returns an {@link TableSortByHitEntry} for a given {@link #getSortByAttributeName() attribute name}.
   *
   * @param sortByAttributeName the name.
   * @return an {@link TableSortByHitEntry}
   */
  public static TableSortByHitEntry getEntryForAttributeName(String sortByAttributeName) {

    for (TableSortByHitEntry entry : TableSortByHitEntry.values()) {
      if (entry.sortByAttributeName.equals(sortByAttributeName)) {
        return entry;
      }
    }

    return null;
  }
}
// import io.oasp.gastronomy.restaurant.general.common.api.datatype.OrderBy;
// import io.oasp.gastronomy.restaurant.tablemanagement.common.api.datatype.TableSortByHitEntry;

/**
 * Table sortBy class
 */
public class TableSortBy {

  private TableSortByHitEntry sortByEntry;

  private OrderBy orderBy;

  /**
   * The constructor.
   */
  public TableSortBy() {

    this.sortByEntry = TableSortByHitEntry.ID;
    this.orderBy = OrderBy.ASC;
  }

  /**
   * Returns the field 'sortByEntry'.
   *
   * @return Value of sortByEntry
   */
  public TableSortByHitEntry getSortByEntry() {

    return this.sortByEntry;
  }

  /**
   * Sets the field 'sortByEntry'.
   *
   * @param sortByEntry New value for sortByEntry
   */
  public void setSortByEntry(TableSortByHitEntry sortByEntry) {

    this.sortByEntry = sortByEntry;
  }

  /**
   * Returns the field 'orderBy'.
   *
   * @return Value of orderBy
   */
  public OrderBy getOrderBy() {

    return this.orderBy;
  }

  /**
   * Sets the field 'orderBy'.
   *
   * @param orderBy New value for orderBy
   */
  public void setOrderBy(OrderBy orderBy) {

    this.orderBy = orderBy;
  }

}

Testing endpoints

SOAPUI, JUnit?

Creating Web Services

Clone this wiki locally