piątek, 17 listopada 2017

How to deploy two JHipster apps on one Tomcat server.

In application-prod.yml find section:


spring:
    devtools:
        restart:
            enabled: false
        livereload:
            enabled: false

and replace with:


spring:
    jmx:
        default-domain: your_app_name
    devtools:
        restart:
            enabled: false
        livereload:
            enabled: false

your_app_name must be diffent in each app.

czwartek, 16 listopada 2017

How to deploy JHipster app on Ubuntu 16.04 server in prod profile

1. Install Elasticsearch 2.4.0 form here: https://www.elastic.co/downloads/past-releases/elasticsearch-2-4-0. Check Spring Data Elasticsearch Spring Boot version matrix.

sudo dpkg -i elasticsearch-2.4.0.deb

In etc/elasticsearch/elasticsearch.yml set:
cluster.name: elasticsearch

sudo systemctl enable elasticsearch.service

in /etc/default/elasticsearch make sure these are uncommented:

START_DAEMON=true


sudo /etc/init.d/elasticsearch restart

check: 
curl -XGET 'localhost:9200' 

https://lxadm.com/Problems_starting_elasticsearch_in_Ubuntu_16.04
https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-elasticsearch-on-ubuntu-16-04

2. Install tomcat 7


sudo apt-get install tomcat7

Create setenv.sh in /usr/share/tomcat7/lib/ and add:
CATALINA_OPTS="$CATALINA_OPTS -server -Xms256m -XX:+UseConcMarkSweepGC"

sudo nano /etc/tomcat7/tomcat-users.xml
insert:
  <role rolename="admin-gui"/>
  <role rolename="manager-gui"/>

  <user username="admin" password="password" roles="admin-gui,manager-gui"/>

Install additional packages

sudo apt-get install tomcat7-docs tomcat7-admin tomcat7-examples

restart tomcat

sudo service tomcat7 restart

login

http://server_IP_address:8080

Copy your JHipster app .war file to /var/lib/tomcat7/webapps

Go to http://server_IP_address:8080/manager/html

Start your app.


Check /var/log/tomcat7/catalina.out file for errors.


3. Enabling SSL
Prepare the Certificate Keystore according to: https://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Prepare_the_Certificate_Keystore

Copy .keystore to /etc/tomcat7 and change its owner to tomcat.

In /etc/tomcat7/server.xml uncoment section:
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />

change to:
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS"
keystoreFile="/etc/tomcat7/.keystore" keystorePass="change_it" />


wtorek, 22 sierpnia 2017

How to add translated string to JHipster AngularJS controller

Let’s assume we have already language files in /src/main/webapp/i18n/ and reference for it in state.js.


In controller add $translate to inject section:


1
2
3
4
5
    ManagerSubstituteDialogController.$inject = ['$timeout', '$scope', '$stateParams', '$translate', '$uibModalInstance', 'entity', 
                                                 'ProductSold', 'Product', 'ProductOnStock', 'ManagerSubstitute'];

    function ManagerSubstituteDialogController ($timeout, $scope, $stateParams, $translate, $uibModalInstance, entity, 
               ProductSold, Product, ProductOnStock, ManagerSubstitute) {


Then translated string is:


1
var substituteString=$translate.instant("barfitterApp.productSold.substitute");


czwartek, 15 czerwca 2017

How to add more destinations to SpringBoot WebSocket STOMP app.



This is extension for https://spring.io/guides/gs/messaging-stomp-websocket/ article - you must read it first.

Goal of this version is to add more subscribes and destination for WebSocket.





In original GreetingController was:  

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        Thread.sleep(100); // simulated delay
        System.out.println("HelloMessage message "+ message.getName());
        return new Greeting("Hello, " + message.getName() + "!");
    }



I added more SubscribeMappings:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    @SubscribeMapping("/helloagain")
    @SendTo("/topic/greetings")
    public Greeting greetingAgain(HelloMessage message) throws Exception {
//        greetingAuto(message);
        broadcastMess(message);
        Thread.sleep(1000); // simulated delay
        System.out.println("HelloMessage again message "+ message.getName());
        return new Greeting("Hello again, " + message.getName() + "!");
    }
 
 
 
 
    @SubscribeMapping("/age")
    @SendTo("/topic/age")
    public Greeting greetingAge(HelloMessage message) throws Exception {
        System.out.println("HelloMessage age "+ message.getName());
        return new Greeting(message.getName() + "? Are you really so old?");
    }
 
 
    @SendTo("/topic/greetings")
    public Greeting greetingAuto(HelloMessage message) throws Exception {
        Thread.sleep(1000); // simulated delay
        message.setName(message.getName()+" Auto!");
        System.out.println("Auto HelloMessage message "+ message.getName());
        return new Greeting("Auto Hello, " + message.getName() + "!");
    }
    // greetingAuto nie wysyła do /topic/greetings a jedynie zmienia message
    
    public void broadcastMess(HelloMessage message) throws Exception {
 
        System.out.println("broadcastMess "+ message.getName());
     messaging.convertAndSend("/topic/greetings", new Greeting("broadcastMess " + message.getName() + "!"));
     // javascript musi odbierać tylko Greeting
    }



app.js has two stompClient.subscribe methods and three send methods:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var stompClient = null;
 
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
        $("#ageConversation").show();
    }
    else {
        $("#conversation").hide();
        $("#ageConversation").hide();
    }
    $("#greetings").html("");
//    $("#age").html("");
}
 
function connect() {
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        setConnected(true);
        console.log('Connected: ' + frame);
        stompClient.subscribe('/topic/greetings', function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
        stompClient.subscribe('/topic/age', function (greeting) {
            showAge(JSON.parse(greeting.body).content);
        });
    });
}
 
function disconnect() {
    if (stompClient != null) {
        stompClient.disconnect();
    }
    setConnected(false);
    console.log("Disconnected");
}
 
function sendName() {
    stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}
 
function sendSurName() {
    stompClient.send("/app/helloagain", {}, JSON.stringify({'name': $("#surname").val()}));
}
 
function sendAge() {
    stompClient.send("/app/age", {}, JSON.stringify({'name': $("#age").val()}));
}
 
function showGreeting(message) {
    $("#greetings").append("<tr><td>" + message + "</td></tr>");
}
 
function showAge(message) {
    $("#ageArea").append("<tr><td>" + message + "</td></tr>");
}
 
$(function () {
    $("form").on('submit', function (e) {
        e.preventDefault();
    });
    $( "#connect" ).click(function() { connect(); });
    $( "#disconnect" ).click(function() { disconnect(); });
    $( "#send" ).click(function() { sendName(); });
    $( "#sendsurname" ).click(function() { sendSurName(); });
    $( "#sendage" ).click(function() { sendAge(); });
});



And index.html:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<html>
<head>
    <title>Hello WebSocket</title>
    <link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
    <link href="/main.css" rel="stylesheet">
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
    enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">WebSocket connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">Connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
                    </button>
                </div>
            </form>
        </div>
        <div class="col-md-6">
            <form class="form-inline">
            
                <div class="form-group">
                    <label for="name">What is your name?</label>
                    <input type="text" id="name" class="form-control" placeholder="Your name here...">
                </div>
                <button id="send" class="btn btn-default" type="submit">Send</button>
                
                <div class="form-group">
                    <label for="surname">What is your surname?</label>
                    <input type="text" id="surname" class="form-control" placeholder="Your surname here...">
                </div>
                <button id="sendsurname" class="btn btn-default" type="submit">Send</button>
                
                <div class="form-group">
                    <label for="age">What is your age?</label>
                    <input type="text" id="age" class="form-control" placeholder="Your surname here...">
                </div>
                <button id="sendage" class="btn btn-default" type="submit">Send</button>
                
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="conversation" class="table table-striped">
                <thead>
                <tr>
                    <th>Greetings</th>
                </tr>
                </thead>
                <tbody id="greetings">
                </tbody>
            </table>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="ageConversation" class="table table-striped">
                <thead>
                <tr>
                    <th>Age</th>
                </tr>
                </thead>
                <tbody id="ageArea">
                </tbody>
            </table>
        </div>
    </div>
    </form>
</div>
</body>
</html>




czwartek, 4 maja 2017

How to create new user by admin in JHipster

In Jhipster, by default, user creates account by himself and admin activates it.


If we need to create fully working account by admin at once, some changes are necessary in code.


  1. In src/main/java/.../service/UserService.java

change:


        String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
        user.setPassword(encryptedPassword);
        user.setResetKey(RandomUtil.generateResetKey());
        user.setResetDate(ZonedDateTime.now());
        user.setActivated(true);
        userRepository.save(user);
        userSearchRepository.save(user);
        log.debug("Created Information for User: {}", user);
        return user;


to:


//        String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
//        user.setPassword(encryptedPassword);
//        user.setResetKey(RandomUtil.generateResetKey());
//        user.setResetDate(ZonedDateTime.now());
        user.setPassword(passwordEncoder.encode(managedUserVM.getPassword()));
        user.setActivated(true);
        // new user gets registration key
        user.setActivationKey(RandomUtil.generateActivationKey());
        userRepository.save(user);
        userSearchRepository.save(user);
        log.debug("Created Information for User: {}", user);
        return user;




2. in /src/main/webapp/app/admin/user-management/user-management-dialog.html


change:



    <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true"
                ng-click="vm.clear()">&times;</button>
        <h4 class="modal-title" id="myUserLabel" data-translate="userManagement.home.createOrEditLabel">
            Create or edit a User</h4>
    </div>




to:



    <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true"
                ng-click="vm.clear()">&times;</button>
        <h4 class="modal-title" id="myUserLabel" data-translate="userManagement.home.createOrEditLabel">
            Create or edit a User</h4>
            
            <br>
            <div class="alert alert-danger" ng-show="(vm.user.password !== vm.confirmPassword)" data-translate="global.messages.error.dontmatch">
                The password and its confirmation do not match!
            </div>
    </div>




add:



        
                <div class="form-group">
                    <label class="control-label" for="password" data-translate="global.form.newpassword">New password</label>
                    <input type="password" class="form-control" id="password" name="password" placeholder="{{'global.form.newpassword.placeholder' | translate}}"
                           ng-model="vm.user.password" ng-minlength=4 ng-maxlength=50 required>
                    <div ng-show="form.password.$dirty && form.password.$invalid">
                        <p class="help-block"
                               ng-show="form.password.$error.required" data-translate="global.messages.validate.newpassword.required">
                            Your password is required.
                        </p>
                        <p class="help-block"
                               ng-show="form.password.$error.minlength" data-translate="global.messages.validate.newpassword.minlength">
                            Your password is required to be at least 4 characters.
                        </p>
                        <p class="help-block"
                               ng-show="form.password.$error.maxlength" data-translate="global.messages.validate.newpassword.maxlength">
                            Your password cannot be longer than 50 characters.
                        </p>
                    </div>
                    <password-strength-bar password-to-check="vm.user.password"></password-strength-bar>
                </div>
                <div class="form-group">
                    <label class="control-label" for="confirmPassword" data-translate="global.form.confirmpassword">New password confirmation</label>
                    <input type="password" class="form-control" id="confirmPassword" name="confirmPassword" placeholder="{{'global.form.confirmpassword.placeholder' | translate}}"
                           ng-model="vm.confirmPassword" ng-minlength=4 ng-maxlength=50 required>
                    <div ng-show="form.confirmPassword.$dirty && form.confirmPassword.$invalid">
                        <p class="help-block"
                               ng-show="form.confirmPassword.$error.required" data-translate="global.messages.validate.confirmpassword.required">
                            Your confirmation password is required.
                        </p>
                        <p class="help-block"
                               ng-show="form.confirmPassword.$error.minlength" data-translate="global.messages.validate.confirmpassword.minlength">
                            Your confirmation password is required to be at least 4 characters.
                        </p>
                        <p class="help-block"
                               ng-show="form.confirmPassword.$error.maxlength" data-translate="global.messages.validate.confirmpassword.maxlength">
                            Your confirmation password cannot be longer than 50 characters.
                        </p>
                    </div>
                </div>



and change:

<button type="submit" ng-disabled="editForm.$invalid || isSaving" class="btn btn-primary">



to:



<button type="submit" ng-disabled="editForm.$invalid || isSaving || (vm.user.password !== vm.confirmPassword)" class="btn btn-primary">


środa, 1 lutego 2017

How to pass two parameters in AngularJS ui-sref directive


I have button, where I pass two parameters - id and lastOrderedPosition:

          <button type="submit"
                                    ui-sref="bar-order-opened.add-product({id:orderOpened.id, lastOrderedPosition:orderOpened.productsToOrder.length})"
                                    class="btn btn-primary">
                                <span class="glyphicon glyphicon-plus"></span>
                                <span class="hidden-xs hidden-sm" data-translate="entity.action.add-product">Add</span>
                </button>


Now I need a proper state. Parameters are passed in url:

        .state('bar-order-opened.add-product', {
            parent: 'bar-order-opened',
            url: '/{id}/{lastOrderedPosition}/addprod',
            data: {
                authorities: ['ROLE_BARMAN']
            },
[...]

Id was generated by JHipster to get entity.
lastOrderedPosition it’s additional parameter I need.           
To get it in controller it’s only needed:

vm.lastOrderedPosition = $stateParams.lastOrderedPosition;