jhipster – use description in enums instead of key names

I used jhipster to generate an entity which have a property of type enum.

jhipster entity tipoEstudio
What are the values of your enumeration (separated by comma, no spaces)? SUPERIOR,MEDIA_SUPERIOR,BASICA

because i couldn’t use spaces or special characters (diacritics i.e. accents) in enum names I had to shorten the description a little bit.

It generated the following code

export const enum TipoEducativo {
    SUPERIOR = 'SUPERIOR',
    MEDIA_SUPERIOR = 'MEDIA_SUPERIOR',
    BASICA = 'BASICA'
}

export interface ITipoEstudio {
    id?: number;
    nombre?: string;
    tipoEducativo?: TipoEducativo;
}

export class TipoEstudio implements ITipoEstudio {
    constructor(public id?: number, public nombre?: string, public tipoEducativo?: TipoEducativo) {}
}

The problem was that the values stored in the database came as (note the spaces and diacritics)

    'EDUCACIÓN SUPERIOR',
    'EDUCACIÓN MEDIA SUPERIOR',
    'EDUCACIÓN BÁSICA'

In order to be able to use those values I had to first add a description property in the enum

public enum TipoEducativo {
    SUPERIOR("EDUCACIÓN SUPERIOR"), MEDIA_SUPERIOR("EDUCACIÓN MEDIA SUPERIOR"), BASICA("EDUCACIÓN BÁSICA");

    private String desc;

    TipoEducativo(String desc) {
        this.desc = desc;
    }

    public String getDesc() {
        return desc;
    }

    public static TipoEducativo getTipo(String desc){
        if(desc == null){
            return null;
        }

        for(TipoEducativo tipoEducativo: TipoEducativo.values()){
            if(desc.equals(tipoEducativo.desc)){
                return tipoEducativo;
            }
        }

        throw new IllegalArgumentException("No existe el TipoEducativo para " + desc);
    }

}

And then change the Java class for the Entity by removing the @Enumerated annotation and adding a converter for the enum field.

@Entity
@Table(name = "tipo_estudio")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class TipoEstudio implements Serializable {

    // id, constructor, etc

    @NotNull
    @Convert(converter = TipoEducativoConverter.class)
    @Column(name = "stipo_educativo", nullable = false)
    private TipoEducativo tipoEducativo;

    // getters, setters, etc

}

TipoEducativoConverter.java

@Converter
public class TipoEducativoConverter implements AttributeConverter<TipoEducativo, String> {


    @Override
    public String convertToDatabaseColumn(TipoEducativo tipoEducativo) {
        return tipoEducativo.getDesc();
    }

    @Override
    public TipoEducativo convertToEntityAttribute(String desc) {
        return TipoEducativo.getTipo(desc);
    }
}

With these changes the app was reading correctly the enum type field and was storing the correct description for its value. The only grip I had then was that in the web client UI the underscored descriptions were being showed instead of the long descriptions.

I thought that if I changed the description in the enum type it would use those in the UI, so i changed it like so

export const enum TipoEducativo {
    SUPERIOR = 'EDUCACIÓN SUPERIOR',
    MEDIA_SUPERIOR = 'EDUCACIÓN MEDIA SUPERIOR',
    BASICA = 'EDUCACIÓN BÁSICA'
}

but it changed nothing. I had to change the html and component as follows

<tr *ngFor="let tipoEstudio of tipoEstudios ;trackBy: trackId">
                <td><a [routerLink]="['/tipo-estudio', tipoEstudio.id, 'view' ]">{{tipoEstudio.id}}</a></td>
                <td>{{tipoEstudio.nombre}}</td>
                <td>{{TipoEducativo[tipoEstudio.tipoEducativo]}}</td>
                <!-- button columns --> 
            </tr>

tipo-estudio.component.ts

// other imports
import { ITipoEstudio, TipoEducativo } from 'app/shared/model/tipo-estudio.model';

@Component({
    selector: 'jhi-tipo-estudio',
    templateUrl: './tipo-estudio.component.html'
})
export class TipoEstudioComponent implements OnInit, OnDestroy {
    // other fields
    TipoEducativo = TipoEducativo;

    constructor() {}

    // other methods
}

which led to this error

error TS2476: A const enum member can only be accessed using a string literal.

the key thing is

const in an enum means the enum is fully erased, which means you can’t index it by an arbitrary value. Just remove the const modifier.

as explained here

Finally I removed the const keyword and was able to use the descriptions in the enum instead of key names.

Note however that you still have to replace the code generated by jhipster on select items since they’re hard coded like so.

<select class="form-control" name="tipoEducativo" [(ngModel)]="tipoEstudio.tipoEducativo" id="field_tipoEducativo"  required>
                        <option value="SUPERIOR"> EDUCACIÓN SUPERIOR</option>
                        <option value="MEDIA_SUPERIOR">EDUCACIÓN MEDIA SUPERIOR</option>
                        <option value="BASICA">EDUCACIÓN BÁSICA</option>
                    </select>

sources:
Best approach to JPA enumerated types mapping
Mapping enums with a fixed ID in JPA – An alternative to String and Ordinal
Mapping enums done right with @Convert in JPA 2.1
Get TypeScript enum name from instance
const enum in Typescript
How do the different enum variants work in TypeScript?

Anuncios

ubuntu 18.04 – disable network printer auto discovery

Screenshot from 2019-02-11 11-06-44

Since I upgraded to Ubuntu 18.04 it always bothered me that when I tried to add a specific network printer it would add hundreds of printers in my network (same model so it named them similar).

Well, I found in stackoverflow how to disable auto discovery.

edit /etc/avahi/avahi-daemon.conf and insert below the [server] section this line:

enable-dbus=no

then restart the avahi-daemon service:

sudo service avahi-daemon stop
sudo service avahi-deamon start

The problem later was that the printing dialog in Chrome took a long time to load, so I reverted the configuration back.

jpa – EclipseLink NetBeans Glassfish Exception with lambda functions in Entity

I had to fix a simple getter method for an Entity. I had to return unique elements in an array, so I thought it would be a very good idea to use a stream and the distinct method.

So, instead of having this

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

i was trying to use this

public List<String> getNucsAcumulados(){
        if (nucsAcumulados == null) {
            nucsAcumulados = getCarpetasAcumuladas().stream()
                    .map(Carpeta::getNuc).distinct().collect(Collectors.toList());
        }
        return nucsAcumulados;
    }

but this change threw me an obscure Exception that has nothing to do with it

Caused by: Exception [EclipseLink-7161] (Eclipse Persistence Services – 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Entity class [class mx.gob.queretaro.brujula.tsj.domain.carpetas.CuadernoExhortoRecibido] has no primary key specified. It should define either an @Id, @EmbeddedId or an @IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.

the Class signaled by the Exception already has the Id annotation it was complaining about.

As it happens, if you’re using Glassfish / EclipseLink: EclipseLink <2.6.0 it is a known bug. And you must not use lambda annotations in your entities, period. So i just reverted back the changes to good old java and everything was fine again.

java – signature with private .key encrypted password protected file

The tax administration of my country (México) -Sistema de Administración Tributaria, SAT- gaves us a pair of private and public key for us to authenticate. These are in the form of a file with the .key extension and a file with the .cer extension, respectively. They’re in DER format and the private key is in PKCS8 while the certificate is of type X.509 according to this post.
You can convert your .key file to the PEM format using openssl

openssl pkcs8 -inform DER -in acde970801.key -out
acde970801.pem

We wanted to use this keys, to sign and verify signed documents, in their original format since those are what the end user have. I searched how to do this and found different suggestions. I first tried this post from stackoverflow How to decrypt a private key in Java (without BC openssl)

public PrivateKey decryptKey(byte[] pkcs8Data, char[] password) throws Exception {
    PBEKeySpec pbeSpec = new PBEKeySpec(password);
    EncryptedPrivateKeyInfo pkinfo = new EncryptedPrivateKeyInfo(pkcs8Data);
    SecretKeyFactory skf = SecretKeyFactory.getInstance(pkinfo.getAlgName());
    Key secret = skf.generateSecret(pbeSpec);
    PKCS8EncodedKeySpec keySpec = pkinfo.getKeySpec(secret);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePrivate(keySpec);
}

but it didn’t work, it threw the following exception

Caused by: java.io.IOException: ObjectIdentifier() — data isn’t an object ID (tag = 48)

The funny thing is that I converted the .key file to the PEM format with the above openssl command and then I tried the answer provided in Read RSA private key of format PKCS1 in JAVA and it worked, I just had to make a few changes

private static PrivateKey readPrivateKeyPEM(String filename) throws Exception {
        PEMParser pemParser = new PEMParser(new FileReader(filename));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemParser.readObject();
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }

unfortunately this failed to comply our initial requirement of using the original .key file, also note here that the password is no longer required since we decrypted de key while doing the conversion/export to the PEM format.

Finally I found the solution in How read a PKCS8 encrypted Private key which is also encoded in DER with bouncycastle?.

First, we need to import the required libraries (Bouncy Castle). Note that jdk15on means from jdk 1.5 to 1.8 (java 8).


        
            org.bouncycastle
            bcprov-jdk15on
            1.60
        
        
            org.bouncycastle
            bcpkix-jdk15on
            1.60
        
    

and then we can use the classes to get our private key and do the signing and verification. Our final code is something like this

import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.util.encoders.Base64;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.nio.file.Files;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

public class Main {
    private static PrivateKey readPrivateKeyPEM(String filename) throws Exception {
        PEMParser pemParser = new PEMParser(new FileReader(filename));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemParser.readObject();
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }

    private static PrivateKey readPrivateKeyDER(String filename, String password) throws Exception {
        ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(Files.readAllBytes(new File(filename).toPath()));
        PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(asn1Sequence));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
        PrivateKeyInfo privateKeyInfo = pkcs8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptorProvider);
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }


    private static X509Certificate readCertificate(String filename) throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        return (X509Certificate) certificateFactory.generateCertificate(new FileInputStream(filename));
    }

    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        //PrivateKey privateKey = readPrivateKeyPEM("/tmp/aecc820430qu5.pem");
        PrivateKey privateKey = readPrivateKeyDER("/tmp/aecc820430qu5.key", "s3cr3t");
        X509Certificate cert = readCertificate("/tmp/aecc820430qu5.cer");
        System.out.println("Private key algorithm: " + privateKey.getAlgorithm());
        System.out.println("Private key format: " + privateKey.getFormat());

        File file = new File("/tmp/acuerdo.odt");
        byte[] data = Files.readAllBytes(file.toPath());

        Signature signature = Signature.getInstance("SHA1WithRSA");
        signature.initSign(privateKey);
        signature.update(data);
        byte[] sign = signature.sign();
        System.out.println("Signature: " + new String(Base64.encode(sign)));

        signature.initVerify(cert);
        //signature.update(Files.readAllBytes(new File("/tmp/acuerdo-a.odt").toPath()));
        signature.update(data);
        System.out.println("Firma verificada: " + signature.verify(sign));
    }
}

great!

update(jan 23, 2019): although it worked in a clean empty project, in my current project it was throwing the exception

Caused by: org.bouncycastle.operator.OperatorCreationException: 1.2.840.113549.1.5.13 not available: PBKDF2with8BIT SecretKeyFactory not available
at org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder$1.get(Unknown Source)

I just had to add the provider in the readPrivateKeyDER method

private static PrivateKey readPrivateKeyDER(String filename, String password) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        ASN1Sequence asn1Sequence = ASN1Sequence.getInstance(Files.readAllBytes(new File(filename).toPath()));
        PKCS8EncryptedPrivateKeyInfo pkcs8EncryptedPrivateKeyInfo = new PKCS8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo.getInstance(asn1Sequence));
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
        InputDecryptorProvider decryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(password.toCharArray());
        PrivateKeyInfo privateKeyInfo = pkcs8EncryptedPrivateKeyInfo.decryptPrivateKeyInfo(decryptorProvider);
        PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo);
        return privateKey;
    }

angular – passing a complex (object or array) parameter to a popup dialog

In a jhipster generated app We created a component to confirm an action that is to be done on a batch of selected documents.

You can pass parameters as path params or query params, but most examples only tell you how to pass an id and then resolve it to an object. We already had an array of objects which we want to pass as parameters.
I suppose you could stringify them and pass them as one of the options above, but that would increase the length of the URL; with the advantage it can be shared and state would be restored. Nonetheless we decided to opt for the fourth option described in this answer to the stackoverflow question ‘Send data through routing paths in Angular’.

First we created our component to select the documents that are going to be processed, when the button is pressed we should confirm the action.
We used a PrimeNG table to allow the user to select the documents which we store in our ‘documentosSeleccionados’ variable.
The routerLink specifies it should navigate to a popup dialog, the trick here is we also specified a click action where we tell it to update the value on a Service property -as explained in the answer above-.

lote-firmar.component.html

        <button class="btn btn-primary float-right" (click)="documentoService.documentosSeleccionados = documentosSeleccionados"
                [routerLink]="['/', { outlets: { popup: 'lote/'+ lote.id + '/firmar/confirma'} }]"
                replaceUrl="true"
                queryParamsHandling="merge">
            <fa-icon [icon]="'file-signature'"></fa-icon>
            <span>
            Firmar
            </span>
        </button>
    <br/>
    <p-table #dt [value]="documentos" dataKey="id" selectionMode="multiple" [(selection)]="documentosSeleccionados">
        <ng-template pTemplate="header">
            <tr>
                <th style="width: 3em">
                    <p-tableHeaderCheckbox></p-tableHeaderCheckbox>
                </th>
                <th>Archivo</th>
                <th>
                    <p-dropdown [options]="estatusDocumentos" [style]="{'width':'100%'}" (onChange)="dt.filter($event.value, 'estatus', 'equals')"></p-dropdown>
                </th>
            </tr>
        </ng-template>
        <ng-template pTemplate="body" let-documento>
            <tr>
                <td>
                    <p-tableCheckbox [value]="documento"></p-tableCheckbox>
                </td>
                <td>{{documento.archivo}}</td>
                <td>{{documento.estatus}}</td>
            </tr>
        </ng-template>
    </p-table>

lote-firmar.component.ts

@Component({
    selector: 'jhi-lote-firmar',
    templateUrl: './lote-firmar.component.html'
})
export class LoteFirmarComponent implements OnInit {
    lote: ILote;
    documentos: IDocumento[];
    documentosSeleccionados: IDocumento[];
    estatusDocumentos: SelectItem[];

    ... we inject the Service
    constructor(private activatedRoute: ActivatedRoute, private documentoService: DocumentoService) {}

The service is pretty straightforward, we just need to add the property where we’d like to pass to the popup dialog. We could even specified the type, but we didn’t in this case. Remember services are singletons, so we have just one instance of this class.

documento.service

@Injectable({ providedIn: 'root' })
export class DocumentoService {
    ... other properties
    public documentosSeleccionados;

    constructor(private http: HttpClient) {}

    ... we also have the method to send the objects to process to the server
    firmar(documentos: IDocumento[], keyPassword: string): Observable<EntityArrayResponseType> {
        const options = createRequestOption({ keyPassword });
        return this.http.post<IDocumento[]>(this.resourceUrl + '/firmar', documentos, {
            params: options,
            observe: 'response'
        });
    }
}

Finally, the popup dialog

lote-firmar-dialog.component.html

<form name="loteFirmarForm" autocomplete="off" (ngSubmit)="firmarDocumentos()">
    <div class="modal-header">
        <h4 class="modal-title">Se requiere contraseña</h4>
        ×
    </div>
    <div class="modal-body">

        <p id="jhi-delete-documento-heading">Para firmar los documentos seleccionados, ingresa la contraseña de tu archivo .key</p>
        <div class="col-md-8">
            <div class="form-group">

            </div>
        </div>
    </div>
    <div class="modal-footer">

             <span>Cancelar</span>


             <span>Firmar</span>

    </div>
</form>

since we inject the Documentos service already in this component, we can access the value passed as parameter trough the service and initialize our model in the NgOnInit method.

lote-firmar-dialog.component.ts

@Component({
    selector: 'jhi-lote-firmar-dialog',
    templateUrl: './lote-firmar-dialog.component.html'
})
export class LoteFirmarDialogComponent implements OnInit {
    ... other properties
    documentosSeleccionados: IDocumento[];

    ... this is where we inject the service
    constructor(private documentoService: DocumentoService, public activeModal: NgbActiveModal, private eventManager: JhiEventManager) {}

    ... this is where we read the array passed through the service
    ngOnInit() {
        this.documentosSeleccionados = this.documentoService.documentosSeleccionados;
    }

    ... then we can send this data to the server to be processed in this service
    firmarDocumentos() {
        this.documentoService.firmar(this.documentosSeleccionados, this.keyfilePassword).subscribe(
            response => {
                console.log('se enviaron los documentos a firmar, response:', response);
                this.eventManager.broadcast({
                    name: 'documentosListModification',
                    content: 'Los documentos se firmaron correctamente'
                });
                this.activeModal.dismiss(true);
            },
            error => {
                console.log('ocurrio un error al enviar los documentos a firmar', error);
                this.eventManager.broadcast({
                    name: 'documentosListModification',
                    content: 'Ocurrio un problema al firmar los documentos [' + error + ']'
                });
            }
        );
    }
}

done!

jpa – delete not working

In a jhipster generated entity, the delete method/service was working well, until I added a relationship with another entity as @OneToMany.

@Entity
@Table(name = "lote")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Lote implements Serializable {

    ... other properties

    @OneToMany(mappedBy = "lote", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    @JsonIgnoreProperties("lote")
    private Set<Documento> documentos = new HashSet<>();
    // jhipster-needle-entity-add-field - JHipster will add fields here, do not remove

    ... these methods are added by the generator to keep both entities synced

    public Lote addDocumentos(Documento documento) {
        this.documentos.add(documento);
        documento.setLote(this);
        return this;
    }

    public Lote removeDocumentos(Documento documento) {
        this.documentos.remove(documento);
        documento.setLote(null);
        return this;
    }
    ... getter and setters
}

Then, when called it just didn’t do any deletion from the database.

It is very well explained in this stackoverflow answer.

such behaviour occurs when you have bidirectional relationship and you’re not synchronizing both sides WHILE having both parent and child persisted (attached to the current session).

So, in order to delete a Documento from a Lote, I had to update the service

    @DeleteMapping("/documentos/{id}")
    @Timed
    @Transactional
    public ResponseEntity<Void> deleteDocumento(@PathVariable Long id) {
        log.debug("REST request to delete Documento : {}", id);
        documentoRepository.findById(id).ifPresent(documento -> {
            Lote lote = documento.getLote();
            lote.removeDocumentos(documento);
            documentoRepository.deleteById(id);
        });
        return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id.toString())).build();
    }

there are 2 important things here, one is the use of the @Transactional annotation; and two, we used the removeDocumentos method generated to keep both entities synced.

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!