Author: florian

  • Implementing a simple LoginApp with Vaadin

    If you have a small vaadin application you might want to avoid using complex authentication and authentifaction frameworks like Spring. In this post I want to show a small vaadin application with authentication functionality only using views. This solution might not fullfill complex authorization or authentication functionalities, but it is a simple ways to secure a vaadin interface. The whole application is available on github.

    First we need an object which represents an user and some sort of repository where we can store the user object:

    public class User implements Serializable {
    
      /**
       * 
       */
      private static final long serialVersionUID = 4753508683469842961L;
      
      private String name;
      private String username;
      private String password;
      
      public User() {
        this.name = "";
        this.username = "";
        this.password = "";
      }
      
      public User(String name, String username, String password) {
        this.name = name;
        this.username = username;
        this.password = password;
      }
    
      @Override
      public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((password == null) ? 0 : password.hashCode());
        result = prime * result + ((username == null) ? 0 : username.hashCode());
        return result;
      }
    
      @Override
      public boolean equals(Object obj) {
        if (this == obj)
          return true;
        if (obj == null)
          return false;
        if (getClass() != obj.getClass())
          return false;
        User other = (User) obj;
        if (password == null) {
          if (other.password != null)
            return false;
        } else if (!password.equals(other.password))
          return false;
        if (username == null) {
          if (other.username != null)
            return false;
        } else if (!username.equals(other.username))
          return false;
        return true;
      }
    
      public String getName() {
        return name;
      }
    
      public void setName(String name) {
        this.name = name;
      }
    
      public String getUsername() {
        return username;
      }
    
      public void setUsername(String username) {
        this.username = username;
      }
    
      public String getPassword() {
        return password;
      }
    
      public void setPassword(String password) {
        this.password = password;
      }
      
    }

    The repository class:

    public class UserRepository {
      
      private static HashSet<User> getUsers() {
        HashSet<User> users = new HashSet<User>();
        users.add(new User("John Doe", "john", "amanda"));
        users.add(new User("Amanda Doe", "amanda", "john"));
        
        return users;
      }
      
      public static User authenticate(String username, String password) {
        // We simple create a new user with this credentials and check the hash set if exists.
        User userToCheck = new User("", username, password);
        
        for (Iterator<User> it = getUsers().iterator(); it.hasNext(); ) {
              User f = it.next();
              if (f.equals(userToCheck))
                  return f;
          }
        
        return null;
      }
    }

    The User class implements a simple object which represents a User for login, consisting of name, username and password. This is just an example therefore we will not store the password crypted, never do this in a productive environment! We use username and password to check if an user is equal, or not.

    The UserRepository class should represents a database with stores users, in this case we create two user “John” Doe” and “Amanda Doe” and if we call the autheticate class we simple iterate through the created users until we found the one with the given username and password and return him/her.

    This two classes are implementing the environment, we need to implement our goal.

    The main application consits of four classes:

    • LoginView: Represents the application view before the user has logged in.
    • MainView: The application view after login
    • SimpleLoginApp: The servlet which navigates between the two views.
    • LoginAppUiController: A simple controller which implements the login logic.

    I will start with the the two Views:

    public class LoginView extends VerticalLayout implements View {
    
      /**
       * 
       */
      private static final long serialVersionUID = 3070811273936236288L;
      public static String VIEWNAME = "login";
      
      public LoginView(LoginAppUiController uicontroller) {
        this.setLocale(UI.getCurrent().getLocale());
        this.setSizeFull();
        this.setSpacing(false);
        this.setMargin(false);
        
        VerticalLayout loginWrapper = new VerticalLayout();
        loginWrapper.setWidth(500, Unit.PIXELS);
        loginWrapper.setSpacing(true);
        this.addComponent(loginWrapper);
        this.setComponentAlignment(loginWrapper, Alignment.MIDDLE_CENTER);
        
        TextField username = new TextField();
        username.setWidth(100, Unit.PERCENTAGE);
        username.setPlaceholder("Username");
        username.setIcon(VaadinIcons.USER);
        username.setStyleName("inline-icon");
        username.focus();
        loginWrapper.addComponent(username);
        
        PasswordField password = new PasswordField();
        password.setWidth(100, Unit.PERCENTAGE);
        password.setPlaceholder("Password");
        password.setIcon(VaadinIcons.LOCK);
        password.setStyleName("inline-icon");
        loginWrapper.addComponent(password);
        
        NativeButton loginButton = new NativeButton("Login");
        loginWrapper.addComponent(loginButton);
        loginWrapper.setComponentAlignment(loginButton, Alignment.MIDDLE_CENTER);
        loginButton.addClickListener(listener -> uicontroller.login(username, password));
        loginButton.addShortcutListener(new ShortcutListener("", ShortcutAction.KeyCode.ENTER, null) {
    
          /**
           * 
           */
          private static final long serialVersionUID = 8664364871536035397L;
    
          @Override
          public void handleAction(Object sender, Object target) {
            uicontroller.login(username, password);
          }});
      }
      
      @Override
      public void enter(ViewChangeEvent event) {
        Page.getCurrent().setTitle("SimpleLoginApp - Login");
        }
    }

    Some remarks to the listing above:

    • public class LoginView extends VerticalLayout implements View: This class should implement a vaadin view, therefore we must implemente the View interface.
    • public static String VIEWNAME: This constant is quit important, because if defines the navigation name for this view. We will need the name in the SimpleLoginApp servlet.
    •  If he/she presses on login the LoginAppUiController is called to verify the credentials and to continue.
    public class MainView extends VerticalLayout implements View {
    
      /**
       * 
       */
      private static final long serialVersionUID = 3070811273936236288L;
      public static String VIEWNAME = "main";
      
      public MainView(LoginAppUiController uicontroller) {
        this.setLocale(UI.getCurrent().getLocale());
        this.setSizeFull();
        this.setSpacing(false);
        this.setMargin(false);
        
        VerticalLayout wrapper = new VerticalLayout();
        wrapper.setWidth(500, Unit.PIXELS);
        wrapper.setSpacing(true);
        this.addComponent(wrapper);
        this.setComponentAlignment(wrapper, Alignment.MIDDLE_CENTER);
        
        Label info = new Label("This is the main view of our application, you can reload and close the browser tab, until the session"
            + " does not expire, or you press the logout button, you will always stay on this view.");
        info.setContentMode(ContentMode.HTML); // Set to html to allow line breaks.
        info.setWidth(100, Unit.PERCENTAGE);
        wrapper.addComponent(info);
        
        NativeButton logoutButton = new NativeButton("Logout");
        wrapper.addComponent(logoutButton);
        wrapper.setComponentAlignment(logoutButton, Alignment.MIDDLE_CENTER);
        logoutButton.addClickListener(listener -> uicontroller.logout());
      }
      
      @Override
      public void enter(ViewChangeEvent event) {
        Page.getCurrent().setTitle("SimpleLoginApp - Main");
        }
    }

    Like the listing of the LoginView, we need to implemente the View interface for the MainView as well. Apart of this, we only have a NativeButton which calls the logout method of the LoginAppUiController if pressed.

    The next listing represents the LoginAppUiController, which implements the login logic of the application:

    public class LoginAppUiController implements Serializable {
    
      /**
       * 
       */
      private static final long serialVersionUID = -7570570535339156683L;
      public static String SESSION_ATTRIBUTE = "loginAppUiController";
      
      private User user; // The current user of this instance of the application
      private SimpleLoginApp app; // Our application class
      
      public LoginAppUiController(SimpleLoginApp app) {
        this.app = app;
      }
      
      public void login(TextField username, PasswordField password) {
        // If the username and password combination exists the repository will return the user
        this.user = UserRepository.authenticate(username.getValue(), password.getValue());
        
        // The user has been authenticated therefore we continue to the main application view.
        if(this.user != null) {
          // After login you need to add the current controller to the session.
          UI.getCurrent().getSession().setAttribute(SESSION_ATTRIBUTE, this);
        
          // Finally navigate to the main view of the application
          this.app.getNavigator().navigateTo(MainView.VIEWNAME);
        } else {
          Notification.show("This combination of username and password does not exists", Type.ERROR_MESSAGE);
          username.clear();
          password.clear();
          username.focus();
        }
      }
      
      /**
       * This method checks if the {@link User} is authenticated.
       * @return
       */
      public boolean isAuthenticated() {
        if(user != null)
          return true;
        else
          return false;
      }
      
      /**
       * This method implements the logout procedure of the application
       */
      public void logout() {
        // Close the session, this means we destroy the session including this instance of the LoginAppUiController
            UI.getCurrent().getSession().close();
        
            // Go to the login page 
            UI.getCurrent().getPage().reload();
      }
    }

    Let us go a little bit more into detail of some aspects of this class:

    • public static String SESSION_ATTRIBUTE: We will store the whole LoginAppUiController as session attribute in the vaadin session, under this name.
    • private SimpleLoginApp app: Reference to the servlet which implements the navigation for the two views.
    • public void login(TextField username, PasswordField password): Performs the validation of the user credentials, called from the LoginView. If the credentials are confirmed we add the instance of this LoginAppUiController to the Vaadin session and navigate to the MainView. Otherwise we clear the username and password field and prompt the user to enter its credentials again.
    • public boolean isAuthenticated(): This method checks if the user is authenticated or not.
    • public void logout(): This method is called from the MainView of the application, after the logout button is pressed. It closes the current Vaddin session and forces a reload of the whole UI of the application.

    The last class is the servlet which creates a Vaadin Navigator and implements the java servlet:

    @Theme("mytheme")
    public class SimpleLoginApp extends UI {
      
      /**
       * 
       */
      private static final long serialVersionUID = -8272040115893003478L;
      
      private Navigator navigator;
    
        @Override
        protected void init(VaadinRequest vaadinRequest) {
        		//
        // Create an instance of the LoginAppUiController and add it to the session
        //
        LoginAppUiController uicontroller;
        
        // If a session already exists, there must be an attribute containing the LoginAppUiController
        if(UI.getCurrent().getSession().getAttribute(LoginAppUiController.SESSION_ATTRIBUTE) != null)
          uicontroller = (LoginAppUiController) UI.getCurrent().getSession().getAttribute(LoginAppUiController.SESSION_ATTRIBUTE);
        // Create a new instance of the FrontendUiController, which means the user must enter his/her credentials
        else
          uicontroller = new LoginAppUiController(this);
        
        //
        // Create the navigator
        //
        this.navigator = new Navigator(this, this);
        this.navigator.addView(LoginView.VIEWNAME, new LoginView(uicontroller));
        this.navigator.addView(MainView.VIEWNAME, new MainView(uicontroller));
        
        //
        // Enter the right view
        //
        if(uicontroller.isAuthenticated())
          this.navigator.navigateTo(MainView.VIEWNAME);
        else
          this.navigator.navigateTo(LoginView.VIEWNAME);
        }
    
        @WebServlet(urlPatterns = "/*", name = "SimpleLoginAppServlet", asyncSupported = true)
        @VaadinServletConfiguration(ui = SimpleLoginApp.class, productionMode = false)
        public static class SimpleLoginAppServlet extends VaadinServlet {
    
        /**
         * 
         */
        private static final long serialVersionUID = 4683186830720335264L;
        }
    }
    • This class extends Vaadin UI and therefore implements the method init() and an internal class which extends VaadinServlet.
    • In line 15-24 we create an instance of the LoginAppUiController. First we check if the current session has already an instance of the controller by checking if the session attribute loginAppUiController is not null. Remember we defined the session attribute for this controller in its class. If the attribute is not null we will continue using this instance of the controller class, if not we create a new instance.
    • Next we create a vaadin navigator and add the two view, we have, LoginView and MainView (29-32).
    • Next we decide which view should be loaded. If the user is already authenticated (remember at that point of the application we will have a valid instance of the LoginAppUiController), we will directly navigate to the MainView, else to the LoginView.

    That´s it, now we have a simple Vaadin application with authentication. If there are any questions, just leave a message.

    Links:

  • Checking the current orientation of the image and rotate if necessary

    In one of my current projects I need some basic image operations and want to use as few third part libraries as possible. Especially I do not want to be dependent from server side libraries like ImageMagick.

    In this entry, I want to explain an java implementation which can be used to automatically checks the orientation of an image and correct, if necessary.

    Every image stores several metadata like the date, time, current GPS position and the orientation. This information can be used to check if the image has the right orientation. Especially for images from smartphones this is very important.

    Before continuing it should be noted, that this project stores all files in the database, for this, I use a FileRecord class which consists of the following attributes:

    private long pid;
    private Date creationDate;
    private String filename;
    private String filetyp;
    private int size;
    private byte[] file;
    private String hash;
    private boolean deleted;
    

    First I want to reference to the following stackoverflow discussion, where I got the basic knowledge and several parts of my code. To get more information about EXIF orientation have a look at this entry.

    public static void correctOrientation(FileRecord image) throws ImageProcessingException, IOException, MetadataException {
        String typ = image.getFiletyp().split("/")[0]; // The file typ
        if(typ.equals("image")) {
          Metadata metadata = getImageMetadata(image);
          if(metadata != null) {
            if(metadata.containsDirectoryOfType(ExifIFD0Directory.class)) {
              // Get the current orientation of the image
              Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
              int orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
              
              // Get the current width and height of the image
              int[] imageSize = getImageSize(image);
              int width = imageSize[0];
              int height = imageSize[1];
              
              // Determine which correction is needed
              AffineTransform t = new AffineTransform();
              switch(orientation) {
              case 1:
                // no correction necessary skip and return the image
                break;
                case 2: // Flip X
                    t.scale(-1.0, 1.0);
                    t.translate(-width, 0);
                    transform(image, t);
                    break;
                case 3: // PI rotation 
                    t.translate(width, height);
                    t.rotate(Math.PI);
                    transform(image, t);
                    break;
                case 4: // Flip Y
                    t.scale(1.0, -1.0);
                    t.translate(0, -height);
                    transform(image, t);
                    break;
                case 5: // - PI/2 and Flip X
                    t.rotate(-Math.PI / 2);
                    t.scale(-1.0, 1.0);
                    transform(image, t);
                    break;
                case 6: // -PI/2 and -width
                    t.translate(height, 0);
                    t.rotate(Math.PI / 2);
                    transform(image, t);
                    break;
                case 7: // PI/2 and Flip
                    t.scale(-1.0, 1.0);
                    t.translate(height, 0);
                    t.translate(0, width);
                    t.rotate(  3 * Math.PI / 2);
                    transform(image, t);
                    break;
                case 8: // PI / 2
                    t.translate(0, width);
                    t.rotate(  3 * Math.PI / 2);
                    transform(image, t);
                    break;
                }
            }
          }
        }
      }
    
    private static void transform(FileRecord image, AffineTransform transform) throws IOException {
        String typ = image.getFiletyp().split("/")[0]; // The file typ
        if(typ.equals("image")) {
          // Create a buffered image of the current file
          InputStream in = new ByteArrayInputStream(image.getFile());
          BufferedImage bimage = ImageIO.read(in);
          
          // Create an transformation operation
          AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);
          
          // Create an instance of the resulting image, with the same width, height and image type than the referenced one
          BufferedImage destinationImage = new BufferedImage( bimage.getWidth(), bimage.getHeight(), bimage.getType() );
          op.filter(bimage, destinationImage);
            
            // Set the created image as new buffered image
            image.setFile(getByteArray(destinationImage, image.getFilename()));
        }
      }
    
    public static int[] getImageSize(FileRecord image) throws IOException {
        String typ = image.getFiletyp().split("/")[0]; // The file typ
        if(typ.equals("image")) {
          InputStream in = new ByteArrayInputStream(image.getFile());
          BufferedImage bimg = ImageIO.read(in);
          
          int imageSize[] = {bimg.getWidth(), bimg.getHeight()};
          
          log.debug("Image size: "+bimg.getWidth()+"x"+bimg.getHeight());
          
          return imageSize;
        }
        
        return null;
      }
    
    private static byte[] getByteArray(BufferedImage image, String filename) throws IOException {
        String formatName = "JPEG";
        if(filename.contains(".")) {
          String[] split = filename.split("\\.");
          formatName = split[split.length-1];
        }
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write( image, formatName, baos );
        baos.flush();
        byte[] imageInByte = baos.toByteArray();
        baos.close();
        
        return imageInByte;
      }
    
    public static int[] getImageSize(FileRecord image) throws IOException {
        String typ = image.getFiletyp().split("/")[0]; // The file typ
        if(typ.equals("image")) {
          InputStream in = new ByteArrayInputStream(image.getFile());
          BufferedImage bimg = ImageIO.read(in);
          
          int imageSize[] = {bimg.getWidth(), bimg.getHeight()};
          
          log.debug("Image size: "+bimg.getWidth()+"x"+bimg.getHeight());
          
          return imageSize;
        }
        
        return null;
    }

    Okay, let’s have a deeper look on the code above. As you can see we perform the image orientation correction in several steps:

    1. We extract the information about the current orientation of the image using the metadata-exctractor library. The current orientation of the image is stored as an integer value. See here, for further information.
    2. Next we need the dimension of the image using the getImageSize(FileRecord image) method.
    3. Now we use the AffineTransform class from the java.awt.geom package to set the required rotation. If the current orientation value is 1 we do not to need to perform an operation. For each other value we do need and for this we use the transform(FileRecord image, AffineTransform transform) method. This method creates a new BufferedImage and performs the right AffineTransformOp operation on it, which corrects the orientation. In the last line of the transform(FileRecord image, AffineTransform transform) method we store the resulted image as byte array in the FileRecord object.

    That´s it, now our image has the right orientation.

  • Extract the GPS information of an image

    In one of my current project I need some basic image operations and want to use as few third part libraries as possible. Especially I do not want to be dependent from server side libraries like ImageMagick.

    In the following blog entries I want to show a java implementation for some frequently uses image operations/tranfomrations. This time I want to go into detail how to extract the GPS information from an image, if exists.

    Before continuing it should be noted, that this project stores all files in the database, for this, I use a FileRecord class which consists of the following attributes:


    private long pid;
    private Date creationDate;
    private String filename;
    private String filetyp;
    private int size;
    private byte[] file;
    private String hash;
    private boolean deleted;

    As already mentioned I wanted to use as few third part libraries as possible, but the Exif information of the image is a basic requirment which is needed to continue. For this task I decided to use the metadata-extractor java library, which I already used in another project, and I really like because of its simplicity.

    But now let us start with the implemenation of the operations:


    public static Metadata getImageMetadata(FileRecord image) throws ImageProcessingException, IOException {
    String typ = image.getFiletyp().split("/")[0]; // The file typ
    if(typ.equals("image")) {
    InputStream in = new ByteArrayInputStream(image.getFile());
    Metadata metadata = ImageMetadataReader.readMetadata(in);

    return metadata;
    } else
    return null;
    }

    public static String[] getGeoLocation(FileRecord image) throws ImageProcessingException, IOException {
    String coordinates[] = new String[3];
    Metadata metadata = getImageMetadata(image);
    if(metadata != null) {
    if (metadata.containsDirectoryOfType(GpsDirectory.class)) {
    GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
    GpsDescriptor gpsDesc = new GpsDescriptor(gpsDir);

    // Set Latitude
    coordinates[0] = gpsDesc.getGpsLatitudeDescription();
    // Set Longitude
    coordinates[1] = gpsDesc.getGpsLongitudeDescription();
    // Set Altitude
    coordinates[2] = gpsDesc.getGpsAltitudeDescription();
    }
    }
    return coordinates;
    }

    These two methods uses the already mentioned metadata-extractor java library to extract the image metadata, from a given FileRecord if of type “image”. For more information about metada and the library please visit the git-hub page of the developer, the readme is quiet well written.