java – detached entity passed to persist

We use jhipster as a template for one of our applications.
We have a related entity of User using a OneToOne relationship with extra information as suggested here.

The following code tries to find the user, which already must exist, and then find the related ‘extra’ information, a keyfile in this particular case, -if found- it should update it and if not it should create it and store it in the database.

@PostMapping("/key/{login:" + Constants.LOGIN_REGEX + "}")
    @Timed
    public ResponseEntity<UserKeyFile> updateKeyFile(@PathVariable String login, @RequestPart("file")MultipartFile file) {
        log.debug("peticion REST para actualizar el archivo key del usuario : {}", login);
        File fileSaved = saveToFile(file, login);

        return ResponseUtil.wrapOrNotFound(
            this.userRepository.findOneByLogin(login).flatMap(
                user -> Optional.of(
                    this.userKeyFileRepository.findById(user.getId()).map(userKeyFile -> {
                        userKeyFile.setLastModifiedDate(Instant.now());
                        return userKeyFile;
                    }).orElseGet(() -> {
                        UserKeyFile userKeyFile = new UserKeyFile();
                        userKeyFile.setUser(user);
                        userKeyFile.setRuta(fileSaved.getAbsolutePath());
                        return this.userKeyFileRepository.save(userKeyFile);
                    })
                )
            )
        );
    }

at a first attempt it threw the following error

2018-12-06 09:10:15.825 ERROR 14686 — [ XNIO-2 task-3] m.g.t.aop.logging.LoggingAspect : Exception in com.contoso.web.rest.UserKeyFileResource.updateKeyFile() with cause = ‘org.hibernate.PersistentObjectException: detached entity passed to persist: com.contoso.domain.User’ and exception = ‘detached entity passed to persist: com.contoso.domain.User; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.contoso.domain.User

as it happens, we were using two different repositories and hence the error. We just need to add the org.springframework.transaction.annotation.Transactional annotation

@Transactional
public ResponseEntity updateKeyFile(@PathVariable String login, @RequestPart(“file”)MultipartFile file) {

and now we can persist our related Entity. great!

Anuncios

glassfish – add certificate to trust store

We changed our SMTP server recently, which is used by apps to send notification emails. The problem was that the certificate it was using was issued by itself, hence when adding it to a mail client, i.e. thunderbird, it asks you to confirm the exception before it could send any mail (it requires STARTTLS).

So, in order for our apps running on a Glasfish 4.1 Server to send emails, we need to add the certificate to the trust store.

When attempting to send an email with the following code

public void sendMail(String to, String subject, String body) {
        try {
            // sets SMTP server properties
            Properties properties = new Properties();
            properties.put("mail.smtp.host", SMTP_HOST);
            properties.put("mail.smtp.port", SMTP_PORT);
            properties.put("mail.smtp.auth", "true");
            properties.put("mail.smtp.starttls.enable", "true");
            Authenticator auth = new Authenticator() {
                public PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(SMTP_USER, SMTP_PASSWORD);
                }
            };
            Session session = Session.getInstance(properties, auth);
            MimeMessage msg = new MimeMessage(session);

            msg.setFrom(new InternetAddress(SMTP_USER));
            InternetAddress[] toAddresses = {new InternetAddress(to)};
            msg.setRecipients(Message.RecipientType.TO, toAddresses);
            msg.setSubject(subject);
            msg.setSentDate(new Date());
            msg.setText(body, "utf-8", "html");

            Transport.send(msg);
        } catch (MessagingException e) {
            String error = String.format("Ocurrió un problema al enviar un CORREO a: %s", to);
            log.error(error, e);
        } catch (Exception e) {
            String error = String.format("Ocurrió un problema al enviar un CORREO a: %s", to);
            log.error(error, e);
        }
    }

The error logged on Glassfish was

Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

First get the certificate of the SMTP server. We got it through the web client accessing with Chrome and clicking on the ‘Not Secure’ label, then Certificate | Details | Export… /tmp/smtp.cer

Then we need to import it with the keytool.

To get the path where the trusted certs are stored I ran the command

ps aux | grep glass

and identified the line

-Djavax.net.ssl.trustStore={glassfish}/domains/domain1/config/cacerts.jks

Now it’s time to import the certificate. First do a backup.

cp {glassfish}/domains/domain1/config/cacerts.jks {glassfish}/domains/domain1/config/cacerts.jks.bkp

Then import the certificate

keytool -import -trustcacerts -alias smtp -file /tmp/smtp.cer -keystore {glassfish}/domains/domain1/config/cacerts.jks

done! now you can send emails through this SMTP server.

ubuntu – from 16.04 to 18.04

ubuntu bionic beaver logo

I used Ubuntu 16.04 for as long as I could. It turns out that Dropbox sent me a notification about dropping support for file systems different than ext4.

your Dropbox folder will stop syncing because it’s on a file system or partition that no longer meets the requirements

I thougth I was using ext4 but, since I had enabled encryption on my home folder, in reality I had ecryptfs. So I needed to use another partition if I’d like to continue using Dropbox.
I thought it wouldn’t be so hard to resize my primary partition -since I used LVM- and create another partition with ext4. Well I tried system-config-lvm and kvpm but they both required to boot from a liveusb or livecd distribution since the partition I wanted to resize was my root partition.

So I decided it would be easier, and perhaps better, for me to just upgrade to ubuntu 18.04 LTS.

I wasn’t wrong, the new version feels smoother, faster and a little bit cleaner.

The only hiccups I had were

  • no fullscreen apps (hide menu bar when maximized)
    Although you can install this gnome extension to enable this behavior.
  • HUD is gone
    I used it with gimp a lot -crop, resize, any menu command available just pressing ALT-. Well, there’s also a gnome extension for this.
  • hot corners (to change between applications pointing the mouse to a corner or CMD+W)
    You can use a gnome extension called custom corner as explained in this article.
  • adding custom launchers to dock
  • it doesn’t have a default backup manager (you can install deja-dup as it was the default before)
    > sudo apt install deja-dup
  • i needed support for exfat (it’s where I placed my last backup)
    > sudo apt install exfat-fuse exfat-utils

well, so far I’m very happy with the upgrade.

bootstrap space between labels in angularjs

I wanted to use labels from bootstrap 3.3 to denote tags in an element. I had an ng-repeat directive to add a label for each tag, but there was no spacing between labels.

<a ui-sref="tag.details({tag: tag})" ng-repeat="tag in post.tags"><span class="label label-default">{{tag}}</span></a>

I found that if you don’t have spaces (could be a new line) between labels or -the opposite- you have trailing spaces inside the label element, it won’t add spacing between them.

You can test it in this jsbin

Since I don’t know how to add a new line between each ng-repeat element I had to use the style attribute to add a margin between them. Besides I was using a link on each tag to transition state to the details, so I needed it anyway to style up a little bit.

<a ui-sref="tag.details({tag: tag})" ng-repeat="tag in post.tags" style="color: white;"><span class="label label-default" style="margin-left: 5px;">{{tag}}</span></a>

sources:
https://stackoverflow.com/questions/24380129/no-spacing-between-bootstrap-labels
https://forum.pagelines.com/topic/23014-how-to-make-bootstrap-labels-and-badges-become-a-link-like-button/

ngToast in angularjs with jhipster

We have an application which was created with jhipster and using angular 1.4.5. We wanted to show notificactions without disrupting the user with modals. We found ngToast which looked right to our needs. Let’s see how you can configure it.

First add the required dependencies using bower (I know it’s retired but it was used before. You can omit this step and just include the files in the index.html file).

cd webapp/
bower install ngToast

then add the files to your index.html file

index.html

<head>
<link rel="stylesheet" href="bower_components/ngToast/dist/ngToast.min.css" />
</head>
<body>
http://bower_components/ngToast/dist/ngToast.min.js
</body>

we can configure the location of notification messages on screen in the file app.js

'use strict';

angular.module('myApp', ['LocalStorageModule', 
    'ngResource', 'ui.router', 'ngCookies', 'color.picker', 'ngAria', 'ngCacheBuster', 'ngFileUpload', 'infinite-scroll', 'ngToast'])
    .run(function ($rootScope, $location, $window, $http, $state,  Auth, Principal, ENV, VERSION) {            
        ...
    })
    .config(['ngToastProvider', function (ngToast) {
        ngToast.configure({
            verticalPosition: 'bottom',
            horizontalPosition: 'right'
        });
    }])
;

an then use it in our controller

'use strict';

angular.module('myApp')
        .controller('myController', function ($scope, $compile, $element, 
            Principal, ngToast) {
            ...
            $scope.showSuccess = function () {              
                    ngToast.create({
                        className: 'success',
                        dismissButton: true,
                        compileContent: true,
                        content: 'Great! your file was saved.'
                    });
                });
            };            
        });

sources:
http://tamerayd.in/ngToast/#
https://github.com/tameraydin/ngToast/issues/10#issuecomment-64061276

EclipseLink error: Entity class has no primary key specified. It should define either an @Id, @EmbeddedId or an @IdClass

I had an entity working perfectly until I added a ‘custom’ getter, to return an object array as a string array.

This entity had the @Id attribute properly set, but when deploying, it threw me this nasty error

Entity class [class …] has no primary key specified

I discovered after some time wondering what had just happened, that if you use EclipseLink < 2.6.0 and deploy to Glassfish (very common if you use Netbeans IDE), if you add lambda expressions on entities it just goes wrong. bug

This is my entity class and the seemingly innocent culprit method

@Entity
public class Carpeta implements Serializable {

    @Id
    @NotNull
    @Column(name = "ID")
    private String id;

    ... other fields

    @OneToMany(mappedBy = "acumulada")
    @JsonIgnore
    private Collection<Carpeta> carpetasAcumuladas;

    public Carpeta() {
    }

    ... other getter and setters

    public Collection<Carpeta> getCarpetasAcumuladas() {
        return carpetasAcumuladas;
    }

    /** custom getter using lambda expression **/
    public List<String> getNucsAcumulados(){
        return getCarpetasAcumuladas().stream().map(carpeta -> {
            return carpeta.getNuc();
        }).collect(Collectors.toList());
    }

}

to solve the issue, just remove lambda expressions from your entities if you're using EclipseLink < 2.6.0 on Glassfish.

In our case

    public List<String> getNucsAcumulados(){
        List<String> nucs = new ArrayList<>();
        for (Carpeta carpeta : getCarpetasAcumuladas()) {
            nucs.add(carpeta.getNuc());
        }
        return nucs;
    }

we can cache the array instead of returning a new
array each time

    private List<String> nucsAcumulados = null;

    public List<String> getNucsAcumulados(){
        if (nucsAcumulados == null) {
            nucsAcumulados = new ArrayList<>();
            for (Carpeta carpeta : getCarpetasAcumuladas()) {
                nucsAcumulados.add(carpeta.getNuc());
            }
        }
        return nucsAcumulados;
    }

sources:
https://stackoverflow.com/a/35623026/961652
https://bugs.eclipse.org/bugs/show_bug.cgi?id=429992

birt report drill down url window location base path problem

You can navigate to the details of a certain data category in a BIRT report using the hyperlink property of an element and use the drill down option to configure it.

Selection_007

Hyperlink Options _006

the problem for us was that when deploying the report to an instance of the ReportViewer it added some wrong path prefix to the report path variable ‘__report’.

So we had to construct the path ourselves. First change the drill-trough option to URI, and the Location to javascript syntax.

We tried to access the window object from a script in the report (since the scripts are javascript) and use it to find out the base url and path, but it failed complaining that ‘window’ was not defined. I suppose you can’t access the window object, I couldn’t find one.

importPackage(Packages.java.text);
sdf = new SimpleDateFormat('yyyy-MM-dd');

window.location.origin + window.location.pathname +
'?__format=pdf&__report=report/tsj/DetallesIndicadores/DetalleCarpetasIniciadas.rptdesign' +
'&desde=' + sdf.format(params['desde']) +
'&hasta=' + sdf.format(params['hasta'])

But it turns out, as I discovered through this forum post, you can use the report context object to access the HttpServletRequest which we can use to construct our URL.

Then you just have to format and add your parameters to the URL.

Ours ended up like this

importPackage(Packages.java.text);
sdf = new SimpleDateFormat('yyyy-MM-dd');

reportContext.getHttpServletRequest().getRequestURL().toString() +
'?__format=pdf&__report=report/tsj/DetallesIndicadores/DetalleCarpetasIniciadas.rptdesign' +
'&desde=' + sdf.format(params['desde']) +
'&hasta=' + sdf.format(params['hasta'])

remember the last line is returned as the value for our URL, in this case the concatenation of our values.

sources:
https://www.eclipse.org/forums/index.php/m/1235339/?srch=window.location#msg_1235339
https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.birt.doc.isv%2Fenginescript%2Fapi%2Forg%2Feclipse%2Fbirt%2Freport%2Fengine%2Fapi%2Fscript%2FIReportContext.html
https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html
https://stackoverflow.com/questions/1629102/root-url-of-the-servlet