Welcome to DLIBRARY’s documentation!

Team:

1409 DLIBRARY

Members:
  • Besim Ongun Kanat - 150120047
  • Furkan Aksın - 150130701
  • Mehmet Taner Ünal - 150130702
  • Ozan Özyeğen - 150120115
  • Müşerref Ebru Özaltın - 040080241

DLIBRARY is a simple book collection manager application. It helps users collect their book collection information in the database The book database can manage various author, publisher and dealer information. It gives admin user permission to change the list of authors, publishers and dealers as well as list of books. Admin user can also edit the user list and their info.

In this project we used a SQLite database with Apache’s Wicket Framework to build it. We created several collections to manage records in database.

Contents:

User Guide

DLIBRARY has a simple UI. Most of its functionality needs authentication.

This is the homepage:

Main page

Home page of DLIBRARY.

Parts Implemented by Besim Ongun Kanat

User and Login System

New users can register themselves in Registration page

Registration

After registration users can easily login from navigation bar:

Navbar login

According to data taken from user it will show the one of the messages below:

_images/loginErr.png
_images/loginSuccess.png

My Collection option & user info will be visible after a successful login.

Navbar after login
Navbar User Info

Administrator users have one extra link in the navbar that goes to User operations page

Admin's navbar

Administrators can add, edit and delete users

Users page
Add a user
Edit a user
Delete users

User’s Book Collection

In the book list user can add book to his/her collection via the link in Book List page

Add to my collection button

And remove them in the My Collection Page

Remove from colletion

Advanced Book Filtering

In the books page there is a filter box. Users can search a book by name, author, genre, publisher, ISBN, year and dealer or any combination of them

Book filtering

The results are shown in the results page:

Book search, results

Parts Implemented by Furkan Aksın

Genre, Summary and Keyword

Genres can be listed in Genres tab on Navigation Bar. Users can see all the genres in that list.

Genre List

Admin can see Genres list with chekcboxes and Add and Delete Buttons.

Admin Genre List

Admin can add, delete or edit any genres.

Add Genre
Delete Genre
Edit Genre

Keywords are shown in Book Display Page.

Keywords

Summaries are just placed in SQL Database. They are not shown on interface.

Summaries

Parts Implemented by Mehmet Taner Ünal

Publishers

Publishers can be listed in Publishers tab on Navigation Bar. Users can see all the publishers on the list.

Publisher List

Only admin can see and use checkboxes, Add, Delete and Edit Buttons on Publisher List.

Publisher List for Admin

Add, Delete and Edit features can be used by Admin.

Add a Publisher
Edit a Publisher

Book Information Page

Informations of the books can be seen by just clicking on them.

Books Info Link Page

Book informations like author, publisher, ISBN, available dealers, genres, keywords can be seen on Book Info page.

Books Info Page

Parts Implemented by Ozan Özyeğen

Dealer, Price and Rent Info

Dealers can be listed by clicking the Dealers tab on the Navigation Bar. Users can only see the Dealers List.

Dealer List

Admins can see Dealers List with the checkboxes and Add, Delete and Edit Buttons.

Admin's Dealer List

Admin can add, delete or edit any Dealer.

Add Dealer
Delete Dealer
Edit Dealer

Prices of the books are shown in the Book List Page.

Price

Rent info is just placed in SQL Database. It is not shown on the Interface

Rent

Parts Implemented by Müşerref Ebru Özaltın

Developer Guide

Database Design

We use a simple SQLite database whose diagram is below:

E/R diagram

Git Workflow

In the coding stage we often used GIT’s branching model. It helped us to work parallel. We have created our own branches and as seen below there are many merges to master branch.

E/R diagram

Parts Implemented by Besim Ongun Kanat

Initialization

The startup code initializes the database file for use by reading and executing statements that are in init.sql file Then it will create collections for general use. Our class extends a special type of Application from Wicket library called AuthenticatedWebApplication because we use user authentication.

public class WicketApplication extends AuthenticatedWebApplication
{
 private IUserCollection userCollection;
 private IAuthorCollection authorCollection;
 private IPublisherCollection publisherCollection;
 private IDealerCollection dealerCollection;
 private IBookCollection bookCollection;
 private IGenreCollection genreCollection;
 private Connection database;

 @Override
 public void init()
 {
     super.init();
     String homeDir = System.getProperty("user.home");
     String dbPath = homeDir + File.separator + "dlibrary.sqlite";

     File dbFile = new File(dbPath);
     if (dbFile.exists())
         dbFile.delete();

     try
     {
         Class.forName("org.sqlite.JDBC");
     }
     catch (ClassNotFoundException exc)
     {
         throw new UnsupportedOperationException(exc.getMessage());
     }

     try
     {
         this.database = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
     }
     catch (SQLException exc)
     {
         throw new UnsupportedOperationException(exc.getMessage());
     }

     // Auto Database Creation Code
     ServletContext context = getServletContext();
     InputStream stream = context.getResourceAsStream("/WEB-INF/init.sql");
     BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
     String line;
     try
     {
         while ((line = reader.readLine()) != null)
         {
             Statement stmt = database.createStatement();
             stmt.executeUpdate(line);
             stmt.close();
         }

     }
     catch (IOException exc)
     {
         throw new UnsupportedOperationException(exc.getMessage());

     }
     catch (SQLException exc)
     {
         throw new UnsupportedOperationException(exc.getMessage());
     }

     userCollection = new UserCollectionJDBC(database);
     authorCollection = new AuthorCollectionJDBC(database);
     publisherCollection = new PublisherCollectionJDBC(database);
     dealerCollection = new DealerCollectionJDBC(database);
     bookCollection = new BookColletionJDBC(database);
     genreCollection = new GenreCollectionJDBC(database);
 }

 @Override
 public Class<? extends WebPage> getHomePage()
 {
     return HomePage.class;
 }

 @Override
 protected Class<? extends WebPage> getSignInPageClass()
 {
     return LoginPage.class;
 }

 @Override
 protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass()
 {
     return DLibraryLoginSession.class;
 }

 //GETTERS AND SETTERS
}
Abstract Collection

AbstractJDBCCollection is a simple helper abstract class its only purpose is preventing collections repeat the code of connection process again and again. The clases that extend AbstractJDBCCollection need to implement a constructor which takes an Connection type argument. The class will provide _db variable for subclasses.

/*
 * Abstract class for creating collections with Database connection
 * Written in order not to rewrite Connection code again and again.
 *
 * Group members should implement Collection classes extending this
 * class.
 */

public abstract class AbstractJDBCCollection
{
    protected Connection _db;

    public AbstractJDBCCollection(Connection dbConnection)
    {
        try
        {
            if (!dbConnection.isClosed())
                _db = dbConnection;
            else
                throw new UnsupportedOperationException("Connection that was sent is closed");
        } catch (SQLException exc)
        {
            throw new UnsupportedOperationException(exc.getMessage());
        }
    }
}
Login Process

The login check is performed by DLibraryLoginSession class. It extends AuthenticatedWebSession from Wicket library. authenticate method is the key here. Wicket provides a level of abstraction here and we only need to create a method that returns true or false, almost all session operations are handeled by Wicket. The method calls the checkUserLogin method of IUserCollection interface, which is implemented by UserCollectionJDBC, then load user info to session.

public class DLibraryLoginSession extends AuthenticatedWebSession
{
    private User loggedUser = null;

    // .......... CONSTRUCTOR

    @Override
    public boolean authenticate(String username, String password)
    {

        WicketApplication app = (WicketApplication) getApplication();
        if (app.getUserCollection().checkUserLogin(username, password))
        {
            loggedUser = app.getUserCollection().getUserByName(username);
            return true;
        }
        else
        {
            return false;
        }
    }

    // ....... GETTERS AND EMPTY-IMPLEMENTED METHODS

}
User Collection

The UserCollectionJDBC class that implements IUserCollection interface, provides simple user operations add, edit, delete, get a list of users and lookup a user with a particular name. checkUserLogin is a special method that checks wheter a particular username and password matches.

public interface IUserCollection
{
    public List<User> getUserList();

    public User getUserByName(String name);

    public boolean checkUserLogin(String name, String password);

    public void addUser(User user);

    public void deleteUser(User user);

    public void updateUser(User user, User newUserData);
}
Book filtering

The filtering operation in BookCollectionJDBC is done with intersecting different SQL queries. The queries and their keys stored in two arrays and they are added to one last query string and a prepared statement. Then the prepared statement is executed

public class BookColletionJDBC extends AbstractJDBCCollection implements IBookCollection
{
    //OTHER CODES...

    public List<Book> getFilteredBooks(BookFilterOptions filter)
    {
        List<String> queries = new ArrayList<String>();
        List<Object> keys = new ArrayList<Object>();

        if (filter.getTitle() != null)
        {
            queries.add("SELECT * FROM Books WHERE title LIKE ?");
            String[] l = { "%" + filter.getTitle() + "%" };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getAuthor() != null)
        {
            queries.add("SELECT * FROM Books WHERE id IN (SELECT book_id FROM Authorship WHERE author_id = ?)");
            Integer[] l = { filter.getAuthor().getId() };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getDealer() != null)
        {
            queries.add("SELECT * FROM Books WHERE id IN (SELECT book_id FROM Book_Dealers WHERE dealer_id = ?)");
            Integer[] l = { filter.getDealer().getId() };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getPublisher() != null)
        {
            queries.add("SELECT * FROM Books WHERE publisher_id = ?");
            Integer[] l = { filter.getPublisher().getId() };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getGenre() != null)
        {
            queries.add("SELECT * FROM Books WHERE publisher_id = (SELECT book_id FROM Book_Genres WHERE genre_id = ?)");
            Integer[] l = { filter.getGenre().getId() };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getFirstYear() != null)
        {
            queries.add("SELECT * FROM Books WHERE year >= ?");
            Integer[] l = { Integer.parseInt(filter.getFirstYear()) };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getLastYear() != null)
        {
            queries.add("SELECT * FROM Books WHERE year <= ?");
            Integer[] l = { Integer.parseInt(filter.getLastYear()) };
            keys.addAll(Arrays.asList(l));
        }

        if (filter.getIsbn() != null)
        {
            queries.add("SELECT * FROM Books WHERE isbn = ?");
            String[] l = { filter.getIsbn() };
            keys.addAll(Arrays.asList(l));
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < queries.size(); i++)
        {
            sb.append(queries.get(i));
            if (i != queries.size() - 1)
                sb.append(" INTERSECT ");
        }

        try
        {
            PreparedStatement statement = _db.prepareStatement(sb.toString());
            for (int i = 0; i < keys.size(); i++)
            {
                statement.setObject(i + 1, keys.get(i));
            }
            return HandleListStatements(statement);
        }
        catch (SQLException exc)
        {
            throw new UnsupportedOperationException(exc.getMessage(), exc);
        }
    }
    //OTHER CODES..
 }
CSS and HTML Site Design

The CSS of DLIBRARY is totaly handwritten, simple and minimal. There are a few helper classes like .clearfix and layout classes like .center-big. The .well class Is a simple rounded-corner-box with a background that used many places in code. One significant visual addition is a custom list view that is achived by modifying a ul element look like a select element

.center-big
{
    width: 50%;
    margin-left: 25%;
}

.clearfix /* Clearfixing content */
{
    overflow: hidden;
}

.well
{
    overflow: hidden;
    border-radius: 5px;
    clear: both;
    margin: 5px;
    padding: 15px 5px 15px;
    font-size: 18px;
}

.well.red
{
    background-color: #ffb2b2;
    color: #a60000;
}

.well.yellow
{
    background-color: #fff792;
    color: #E04300;
}

form.form ul
{
    border: none;
    border-radius: 7px;
    background: #fff;
    overflow: auto;
    max-height: 100px;
    width: 350px;
    float:left;
    display: block;
    list-style: none;
    padding: 10px;
    margin-bottom: 10px;
}

Parts Implemented by Furkan Aksın

Genre Data Type

Genre Data Type is created with neccessary functions.

public class Genre implements Serializable
{
    private int id;
    private String name;

    public final int getId()
    {
        return id;
    }

    public void setId(int id)
    {
        this.id = id;
    }

    public final String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}
Genre ListView

ListView object is created for listing Genres on the Genres tab.

ListView genreListView = new ListView("genre_list", genres)
{
   protected void populateItem(ListItem item)
   {
       final Genre genre = (Genre) item.getModelObject();
       item.add(new Label("name", genre.getName()));
       Link editGenre = new Link("editLink")
       {
           @Override
           public void onClick()
           {
               setResponsePage(new GenreAddEdit(genre, false));
           }
       };
       Check checkBox = new Check("selected", item.getModel());
       checkBox.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
       editGenre.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
       item.add(editGenre);
       item.add(checkBox);
   }
};
IGenreCollection
public interface IGenreCollection
{
    public List<Genre> getGenreList();

    public Genre getGenreByName(String name);

    public void addGenre(Genre genre);

    public void deleteGenre(Genre genre);

    public void updateGenre(Genre genre, Genre newGenreData);

}
GenreCollectionJDBC
public List<Genre> getGenreList()
{
  LinkedList<Genre> list = new LinkedList<Genre>();
  try
  {
      Statement statement = _db.createStatement();
      ResultSet results = statement.executeQuery("SELECT id, name FROM Genres");
      while (results.next())
      {
          Genre current = new Genre();
          current.setId(results.getInt("id"));
          current.setName(results.getString("name"));
          list.add(current);
      }
      results.close();
      statement.close();
  }
  catch (SQLException exc)
  {
      throw new UnsupportedOperationException(exc.getMessage());
  }

  return list;
}

Parts Implemented by Mehmet Taner Ünal

Book Information Page

In the BookInfo.java file, required labels are created for BookInfo.html. So when the books are clicked, directly this page will be opened with the book’s informations. Informations are reached using book argument.

public class BookInfo extends TemplatePage
{
    public BookInfo(Book book)
    {
        final DLibraryLoginSession loginSession = (DLibraryLoginSession) AuthenticatedWebSession.get();
        WicketApplication app = (WicketApplication) getApplication();
        this.add(new Label("isbn", book.getIsbn()));
        this.add(new Label("year", book.getYear()));
        this.add(new Label("publisher", book.getPublisher().getName()));
        this.add(new Label("title", book.getTitle()));

        ListView authorList = new ListView("authorList", book.getAuthors())
        {
            @Override
            protected void populateItem(ListItem subitem)
            {
                subitem.add(new Label("authorName", ((Author) subitem.getModelObject()).getName()));
            }
        };
        this.add(authorList);

        ListView dealerList = new ListView("dealerList", book.getDealers())
        {
            @Override
            protected void populateItem(ListItem subitem)
            {
                subitem.add(new Label("dealerName", ((Dealer) subitem.getModelObject()).getName()));
            }
        };
        this.add(dealerList);

        ListView genreList = new ListView("genreList", book.getGenres())
        {
            @Override
            protected void populateItem(ListItem subitem)
            {
                subitem.add(new Label("genreName", ((Genre) subitem.getModelObject()).getName()));
            }
        };
        this.add(genreList);

        ListView keywordList = new ListView("keywordList", book.getKeywords())
        {
            @Override
            protected void populateItem(ListItem subitem)
            {
                subitem.add(new Label("keywordName", ((Keyword) subitem.getModelObject()).getKeyword()));
            }
        };
        this.add(keywordList);
    }
}
Publisher Collection

The PublisherCollectionJDBC class that implements IPublisherCollection interface, provides simple publisher operations add, update, delete, get a list of publishers and lookup a publisher with a specific name.

public interface IPublisherCollection
{

    public List<Publisher> getPublisherList();

    public Publisher getPublisherByName(String name);

    public void addPublisher(Publisher publisher);

    public void deletePublisher(Publisher publisher);

    public void updatePublisher(Publisher publisher, Publisher newPublisherData);

}
Recommendation Collection

The RecommendationCollectionJDBC class that implements IRecommendationCollection interface, provides simple operations add, update, delete, get a list of all recommendations and lookup a recommended book’s id with a specific book id. Recommendations part could not be completed, but can work fine with direct database inputs.

public interface IRecommendationCollection
{
    public List<Recommendation> getRecommendationList();

    public Recommendation getRecommendationById(int id);

    public void addRecommendation(Recommendation recommendation);

    public void deleteRecommendation(Recommendation recommendation);

    public void updateRecommendation(Recommendation recommendation, Recommendation newRecommendationData);
}

Parts Implemented by Ozan Özyeğen

Dealers List

DealersList.java is created to show all the dealers and provide an interface to admins in a way that they can add, delete and edit dealers.

CheckGroup dealerCheckGroup = new CheckGroup("selected_dealers", selectedDealers);

  ListView DealerListView = new ListView("dealer_list", dealers)
  {

      @Override
      protected void populateItem(ListItem item)
      {
          final Dealer dealer = (Dealer) item.getModelObject();
          item.add(new Label("name", dealer.getName()));
          Link editDealer = new Link("editLink")
          {
              @Override
              public void onClick()
              {
                  setResponsePage(new DealerAddEdit(dealer, false));
              }
          };
          Check checkBox = new Check("selected", item.getModel());
          checkBox.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
          editDealer.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
          item.add(editDealer);
          item.add(checkBox);
      }
  };

  dealerCheckGroup.add(DealerListView);

  Form deleteForm = new Form("deleteDealerForm")
  {
      @Override
      protected void onSubmit()
      {
          for (Dealer current : selectedDealers)
          {
              dealerCollection.deleteDealer(current);
          }

          setResponsePage(new DealerList());
      }
  };

  Label deleteButton = new Label("btnDeleteDealer", "Delete");

  Link addButton = new Link("btnAddDealer")
  {
      @Override
      public void onClick()
      {
          setResponsePage(new DealerAddEdit());
      }
  };
Visibility

Pages are designed in such a way that only admins can see the Add,Delete Button checkboxes and Edit Labels.

Example

checkBox.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
editDealer.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
addButton.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
deleteButton.setVisible(loginSession.isSignedIn() && loginSession.getLoggedUser().isAdmin());
IDealerCollection
public interface IDealerCollection
{
    public List<Dealer> getDealerList();

    public Dealer getDealerByName(String name);

    public void addDealer(Dealer dealer);

    public void deleteDealer(Dealer dealer);

    public void updateDealer(Dealer dealer, Dealer newDealerData);

    public List<PricedDealer> getPricedDealerList();
}
PricedDealer Datatype
public class PricedDealer extends Dealer implements Serializable
{
    private double price;

    public double getPrice()
    {
        return price;
    }

    public void setPrice(double price)
    {
        this.price = price;
    }
}

Parts Implemented by Müşerref Ebru Özaltın