martes, 29 de mayo de 2018

How to convert your Angular 5 app to Universal with ngUniversal

Hi there again! Today I come with a new situation that I solve using Universal in my Angular app with ngUniversal.

The situation:

I developed an application (here is the repository). When I begin using google search engine and other engines to index my web, I've found the problem that my web is not indexed correctly.

When I change the language of the web, the title, the description and the keywords don't change. So google and other engines only index my main index.html file.

With Universal, you can change it, and I'm going to explain how.

The code you have to change:

In my package.json file, I have these dependencies and I show the run you have to define:

"scripts": {
"universal": "ng build --prod client && ng build --prod --app server --output-hashing=false && webpack --config webpack.server.config.js --progress --colors && node dist/server.js",
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
...
"dependencies": {
"@angular/animations": "^5.0.0",
"@angular/common": "^5.0.0",
"@angular/compiler": "^5.0.0",
"@angular/core": "^5.0.0",
"@angular/forms": "^5.0.0",
"@angular/http": "^5.0.0",
"@angular/language-service": "^5.0.0",
"@angular/platform-browser": "^5.0.0",
"@angular/platform-browser-dynamic": "^5.0.0",
"@angular/platform-server": "^5.0.0",
"@angular/router": "^5.0.0",
"@nguniversal/common": "^5.0.0-beta.5",
"@nguniversal/express-engine": "^5.0.0-beta.5",
"@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
"@ngx-translate/core": "8.0.0",
"@ngx-translate/http-loader": "2.0.0"
...

The trick of Universal in Angular is to separate what the browser shows and what the server needs to compile for shows the browser.

You need to create an AppServerModule.ts that use the AppModule.ts of your application:

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';

import { AppModule } from './app.module';
import { TemplateComponent } from '../template/template.component';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TransferState } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { translateFactory } from './translate-universal-loader.service';

@NgModule({
imports: [
// The AppServerModule should import your AppModule followed
// by the ServerModule from @angular/platform-server.
AppModule,
ServerModule,
ModuleMapLoaderModule,
ServerTransferStateModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: translateFactory
}
})
],
// Since the bootstrapped component is not inherited from your
// imported AppModule, it needs to be repeated here.
bootstrap: [TemplateComponent],
})
export class AppServerModule {
constructor() {
console.log('AppServerModule');
}
}

So Universal is separating the Server and the Browser, you have to indicate to your application which parts will be and those parts.

Here you can see my TemplateComponent (that is like an AppComponent):

constructor(@Inject(PLATFORM_ID) private platformId: Object,
public authService: AuthService,
private translate: TranslateService,
private route: ActivatedRoute,
private languageService: LanguageService,
private titleService: Title,
private metaService: Meta) {
this.getLanguanges();
}

ngOnInit() {
if (isPlatformBrowser(this.platformId)) {
// Client only code.
this.loadLanguage();
this.loadMusic();
}
if (isPlatformServer(this.platformId)) {
// Server only code.
this.loadServerLanguage();
}
}

You have to know that some HTML access components are not permitted in Server Part. It's logic, so you don't have access to the dom of the browser. These not permitted uses are for example the access to DOM objects of the HTML or to the navigator.

So this code has to be on the browser part:

if(!userLang || userLang == "") {
userLang = navigator.language;
if(userLang.startsWith("zh")) {
userLang = "zh";
}
}

Also, you need to create a main.server.ts file in the same directory of main.ts with this line:

export { AppServerModule } from './app/core/app.server.module';

Also, you need a tsconfig.server.json file with this content and in the same directory of tsconfig.app.json:

{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"baseUrl": "./",
"module": "commonjs",
"types": []
},
"exclude": [
"test.ts",
"**/*.spec.ts"
],
"angularCompilerOptions": {
"entryModule": "app/core/app.server.module#AppServerModule"
}
}

Also, you need a webpack.server.config.js file like this (just copy-paste):

const path = require('path');
const webpack = require('webpack');

module.exports = {
entry: { server: './server.ts' },
resolve: { extensions: ['.js', '.ts'] },
target: 'node',
// this makes sure we include node_modules and other 3rd party libraries
externals: [/(node_modules|main\..*\.js)/],
output: {
path: path.join(__dirname, 'dist'),
filename: '[name].js'
},
module: {
rules: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
},
plugins: [
// Temporary Fix for issue: https://github.com/angular/angular/issues/11580
// for "WARNING Critical dependency: the request of a dependency is an expression"
new webpack.ContextReplacementPlugin(
/(.+)?angular(\\|\/)core(.+)?/,
path.join(__dirname, 'src'), // location of your src
{} // a map of your routes
),
new webpack.ContextReplacementPlugin(
/(.+)?express(\\|\/)(.+)?/,
path.join(__dirname, 'src'),
{}
)
]
}

And you have to modify your .angular-cli.json file adding a new app. So you are going to have two apps (the client app and the server app). This separation is used on the run script defined on the package.json.

This will be the .angular-cli.json file:

{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "davidmartinezros.com"
},
"apps": [
{
"name": "client",
"root": "src",
"outDir": "dist/browser",
"assets": [
"assets",
"favicon.ico",
"assets/particlesjs-config.json"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
"../node_modules/font-awesome/css/font-awesome.css",
"assets/styles/animations.css",
"assets/styles/buttons.css",
"assets/styles/cookie-warning.css",
"assets/styles/experience.css",
"assets/styles/footer.css",
"assets/styles/loader.css",
"assets/styles/main.css",
"assets/styles/media-queries.css",
"assets/styles/navbar.css",
"assets/styles/projects.css",
"assets/styles/scrollbar.css",
"assets/styles/corner-ribbon.css"
],
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
"../node_modules/tether/dist/js/tether.js",
"../node_modules/popper.js/dist/umd/popper.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"../node_modules/requirejs/require.js",
"assets/scripts/particles.js",
"assets/scripts/particles-loader.js",
"assets/scripts/check-is-on-viewport.js",
"assets/scripts/cookie-warning.js",
"assets/scripts/experience.js",
"assets/scripts/jarallax.js",
"assets/scripts/navigate.js",
"assets/scripts/typewriter-animation.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
},
{
"name": "server",
"platform": "server",
"root": "src",
"outDir": "dist/server",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.server.ts",
"test": "test.ts",
"tsconfig": "tsconfig.server.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [
],
"scripts": [
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}

And finally, you need a server.ts file in the same directory of the package.json file. My server.ts looks like this:

// These are important and needed before anything else
import 'zone.js/dist/zone-node';
import 'reflect-metadata';

import { renderModuleFactory } from '@angular/platform-server';
import { enableProdMode } from '@angular/core';

import * as express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';

(global as any).WebSocket = require('ws');
(global as any).XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;


// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

//app.urlencoded({extended: false});

const PORT = process.env.PORT || 4000;
const HTTPS_PORT = process.env.HTTPS_PORT || 4443;

const KEY_CERTIFICATE = process.env.KEY_CERTIFICATE;
const CRT_CERTIFICATE = process.env.CRT_CERTIFICATE;
const PASSWORD_CERTIFICATE = process.env.PASSWORD_CERTIFICATE;

const DIST_FOLDER = join(process.cwd(), 'dist');

// Our index.html we'll use as our template
const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html')).toString();

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');

// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';

// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
/*
app.engine('html', (_, options, callback) => {
const opts = { document: template, url: options.req.url };

renderModuleFactory(AppServerModuleNgFactory, opts)
.then(html => callback(null, html));
});
*/
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

// Our page routes
export const routes: string[] = [
'main',
'dashboard',
'dashboard/contact',
'dashboard/blog',
'project/:lang/:nom'
];

// All regular routes use the Universal engine
app.get('/', (req, res) => {
console.log(req.query.lang);
console.time(`GET: ${req.originalUrl}`);
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
console.timeEnd(`GET: ${req.originalUrl}`);
});

routes.forEach(route => {
app.get(`/${route}`, (req, res) => {
//res.json({'lang': req.query.lang});
console.log(req.query.lang);
console.time(`GET: ${req.originalUrl}`);
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
console.timeEnd(`GET: ${req.originalUrl}`);
});
app.get(`/${route}/*`, (req, res) => {
//res.json({'lang': req.query.lang});
console.log(req.query.lang);
console.time(`GET: ${req.originalUrl}`);
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req, res } );
console.timeEnd(`GET: ${req.originalUrl}`);
});
});

// Server static files from /browser
app.get('/web', express.static(join(DIST_FOLDER, 'browser'), { 'index': false }));

app.get('/**', express.static(join(DIST_FOLDER, 'browser')));

// All other routes must be resolved if exist
/*
app.get('*', function(req, res) {
res.render(join(req.url), { req });
});
*/

var http = require('http');

var httpServer = http.createServer(app);

// Start up the Node server at PORT
httpServer.listen(PORT, () => {
console.log(`Node server listening on http://localhost:${PORT}`);
});

if(KEY_CERTIFICATE && CRT_CERTIFICATE && PASSWORD_CERTIFICATE) {

var fs = require('fs');
var https = require('https');

var privateKey = fs.readFileSync(KEY_CERTIFICATE, 'utf8');
var certificate = fs.readFileSync(CRT_CERTIFICATE, 'utf8');

var credentials = {
key: privateKey,
cert: certificate,
passphrase: PASSWORD_CERTIFICATE
};
var httpsServer = https.createServer(credentials, app);

// Start up the Node server at HTTP_PORT
httpsServer.listen(HTTPS_PORT, () => {
console.log(`Node server listening on http://localhost:${HTTPS_PORT}`);
});
}

I've defined some URLs of my application that use the ServerModule and the rest of the URLs in the domain access statically. Also, I've defined an HTTP and an HTTPS server for secure connections defined on my node server.

And that's all! Is not easy, but works very good.

I hope you enjoy my tutorial and ask me any question you have with this trick.

You can see my project on production in this URL: https://davidmartinezros.com

Have a nice day!

viernes, 9 de marzo de 2018

How to add music to your Website in Javascript

Hi there!

Today I want to talk about "how to add music to your website with the Html Web API".

We are going to use the Html Audio object instead of other proposed solutions on the net, like use Howler.js or other audio frameworks.

For use this solutions, you need to use Javascript or other language compile Javascript, like Angular or Ionic.

Declaration of variables:

loaded: boolean = false;
playing: boolean = false;
sound: any;

First, create the Audio object and define the default properties:

this.sound = new Audio();
this.sound.autoplay = false;
this.sound.preload = 'auto';
this.sound.autobuffer = true;


Before you define the events, you must declare a local variable pointing to this:

let parent = this;

Then, you have to define the audio event listeners:

this.sound.addEventListener('loadeddata', function() {
console.log("music loaded");
parent.loaded = true;
parent.playTrack();
}, false);


this.sound.addEventListener('play', function() {
console.log("music play");
parent.playing = true;
}, false);


this.sound.addEventListener('pause', function() {
console.log("music pause");
parent.playing = false;
}, false);

And for last you need to define the music src and load it:

this.sound.src = './assets/audio/Rhodesia_MkII.mp3';
this.sound.load();

Now, you need the actions and functions called from the html:

isLoadedTrack() {
return this.loaded;
}

isPlayingTrack() {
return this.playing;
}

playTrack() {
if(this.sound) {
this.sound.play();
}
}

pauseTrack() {
if(this.sound) {
this.sound.pause();
}
}

And the content of the html will look like this:

<div *ngIf="isLoadedTrack()">
      <div class="music" *ngIf="!isPlayingTrack()">
          <button (click)="playTrack()">
<i class="fa fa-play"></i> <i class="fa fa-music"></i>
</button>
      </div>
      <div class="music" *ngIf="isPlayingTrack()">
          <button (click)="pauseTrack()">
<i class="fa fa-pause"></i> <i class="fa fa-music"></i>
</button>
      </div>
</div>

And the result will show those two state:



I hope you find the talk interesting!

You can see the Production use of the Component on https://davidmartinezros.com
And you can take a look to the Portfolio repository: https://github.com/davidmartinezros/portfolio

Any doubt, ask me and I will answer as soon as possible.

viernes, 23 de febrero de 2018

Internationalization in Angular

Here we come again!

Today we will learn how to implement the internationalization in angular easily and in a few steps.

Step 1: Add the necessary libraries to our package.json file.

"dependencies": {
...
"@ngx-translate/core": "8.0.0",
"@ngx-translate/http-loader": "2.0.0",
...
},

Step 2: Configure our application to use a folder of language files.

app.module.ts


import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { HttpClientModule, HttpClient } from '@angular/common/http';


@NgModule({
declarations: [ AppComponent ],
imports: [
...
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
})
...
],
providers: [ ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}

Step 3: Define a component where you can change the language.

language;

languages: Language[] = [
new Language("ca","CATALÀ"),
new Language("es","ESPAÑOL"),
new Language("en","ENGLISH")
];

constructor(private route: ActivatedRoute,
private translate: TranslateService) {
var userLang = "";
this.route.queryParams.subscribe(params => {
if(!params['lang'] || params['lang'] == "") {
userLang = this.language;
} else {
userLang = params['lang'];
}
console.log("queryParams:" + userLang);

if(!userLang || userLang == "") {
userLang = navigator.language;
}

if(userLang) {
userLang = userLang.toLowerCase();
if(userLang.length > 2) {
userLang = userLang.substring(0, 2);
}
}
if(userLang == "ca" || userLang == "es" || userLang == "en") {
this.changeLanguage(userLang);
} else {
this.changeLanguage("ca");
}
});
}

public changeLanguage(language) {

console.log(language);

// canviar l'idioma per defecte usat a les traduccions
this.translate.setDefaultLang(language);
// canviar l'idioma a usar per fer les traduccions
this.translate.use(language);

// canviem l'idioma seleccionat
this.language = language;
}

@NgModule({
imports: [ ... ],
declarations: [ ... ],
exports: [ ...],
providers: [ TranslateService ]
})

Step 4: Use the Pipe translate to get the translations of the language files.

pageComponent.html

...
{{ 'field' | translate }}
...

Step 5: Create the json files with the translations.

en.json


{
"field": "traduction of my first field"
}

Here we could make all the necessary translations in our pages and components.

If we wanted that when choosing a language, the other components and pages of our application were translated, where we had loaded arrays of information, we should do the following:

Step 1: Subscribe an Observable where to load the array.

projects: Project[];

public static updateStuff: Subject<any> = new Subject();

constructor(
private projectService: ProjectService) {
ProjectsComponent.updateStuff.subscribe(res => {
// here fire functions that fetch the data from the api
this.getProjects();
});
}

ngOnInit(): void {
this.getProjects();
}

getProjects(): void {
this.projectService.getProjects()
.then(projects =>
{ this.projects = projects }
);
}

Step 2: Call the Subscription when we change the language.

import { ProjectsComponent } from '../projects/projects.component';

public changeLanguage(language) {
...
ProjectsComponent.updateStuff.next(false);
...
}

I hope you have helped the tutorial.

Any questions, do not hesitate to ask.

miércoles, 7 de febrero de 2018

How to search your web on google.com

Hi,

Like all people know, google have a domain for region. For example, for searching your web on google in Spain you have to use https://www.google.es, for searching your web in USA you have to use https://www.google.com.

But we have a problem using https://www.google.com. Automatically, you will be redirect to your region domain. For example, if you are in Spain, you will be redirect to https://www.google.es.

For resolving this problem, you can access to the url https://www.google.com/ncr and you could use the USA domain and you can investigate your SEO in America and how are you publishing there.

For example, acceding to https://www.google.com/ncr and searching the words david angular java full stack you will see on the first position of the google link list the domain https://davidmartinezros.com/dashboard?lang=en, which is my english web.



And if you search on https://www.google.es and searchs the words david angular java full stack you will see on the first position the domain https://davidmartinezros.com/dashboard?lang=es, which is my spanish web.



If you have any question, ask me on the comments.

jueves, 1 de febrero de 2018

How to Use Forms in Angular 2 passing complex data objects between pages

Hi everybody!

Today we are going to explain how to pass form data object like a complex object between pages in Angular 2 or 4 or 5.

First of all, we have to declare our Data object like an injectable, on this way:

import { Injectable } from '@angular/core';

@Injectable()
export class Data {
    public storage: any;
    public constructor() { }
}

Then, we have to declare our Data object like a provider in our ngModule class file configuration, on this way:

@NgModule({
    declarations: [AppComponent, ...appComponents],
    bootstrap: [AppComponent],
    imports: [
        NativeScriptModule,
        NativeScriptRouterModule,
        NativeScriptRouterModule.forRoot(appRoutes)
    ],
    providers: [Data]
})
class AppComponentModule {}

Then we can use this Data object in our pages.

First of all, we are going to define this in our first page, the origin one:

public constructor(private router: Router, private data: Data) {}

public navigationToPage2() {
   this.data.storage = {
        "firstname": "Nic",
        "lastname": "Raboy",
        "address": {
            "city": "San Francisco",
            "state": "California"
}
   }
   this.router.navigate(["page2"]);
}

And then, we will get the Data object in our second page, which receive the request of our form POST:

public constructor(private data: Data) {
    console.log(JSON.stringify(this.data.storage));
}

Only we need now is to pass our form to the this.data.storage like a JSON file on this way:

this.data.storage = {
    "requestData": JSON.stringify(requestData)
};

And the get the Data on the second page this way:

console.log(this.data.storage.requestData);

I hope you have found it useful and that you have solved your problems.

Try it and have fun!

lunes, 13 de noviembre de 2017

How to Configure Actuator in Spring Boot

Hi,

Today I'm going to explain the steps for configuring Actuator in a Rest Service Spring Boot Application.

It's very easy, only two steps and you have it!

First you have to add the dependency to the pom.xml file:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
</dependency>

The second step is the disable security to the services offers by actuator.

You have to edit the application.properties file and add:

management.security.enabled=false

With this to step, you can run your appication and can call the next services:

http://localhost:8080/health for know the State of your App.

http://localhost:8080/mappings for view your Rest Services published

And you have more services, you can see in your log of your App when you run it.

Try it and have fun!

viernes, 20 de octubre de 2017

What to do for Convert your App into a Progressive Web App

Hi,

I'm going to explain what to do for Converting your App into a Progressive Web App. Or what's the same, for using Service Workers.

Here you can see the demo of a Progressive Web App with Service Workers.

It's very easy.

You have to use Angular/Cli in your Angular App and do the next three steps:

- First, you have to change your Angular/Cli config file and add the next line:

{
"apps": [
{ "serviceWorker": true } ]
}


- Second, you have to install the Service Workers dependency:

npm i @angular/service-worker --S

- And finally, you have to add the external links from your index.html for use the cache on them, modifying the generated file ngsw-manifest.json:

"external": {
"urls": [
{"url": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"},
{"url": "https://cdn.rawgit.com/konpa/devicon/4f6a4b08efdad6bb29f9cc801f5c07e263b39907/devicon.min.css"},
{"url": "https://fonts.googleapis.com/icon?family=Material+Icons"},
{"url": "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"}
]
},


And that's all!

Try it and have fun!

lunes, 16 de octubre de 2017

How to define repository ownership in JPA with Spring Security?

Hi, everybody!

Today I'm going to explain how to define repository ownership in JPA with Spring Security.

First of all, we are going to explain what is a repository: is an interface of JPA that provide the access to a database table modeled with hibernate.

In this interface, you can define any JPA or native query and you can change the behaivour of any call.

For example, you can have this:

@Repository
public interface MyHibernateModelClassRepository extends JpaRepository<MyHibernateModelClass, Long> {

}


With this implementation, you can call any CRUD method.

Now, I'm going to explain how to define ownership of this JPA interface:

With the same example, you can have this:

@Repository
public interface MyHibernateModelClassRepository extends JpaRepository<MyHibernateModelClass, Long> {

    @PreAuthorize("#entity == null || #entity.id == principal.id")
    @Override
    <S extends MyHibernateModelClass> S save(S entity);

    @PreFilter("filterObject == null || filterObject.id== principal.id")

    @Override
    <S extends MyHibernateModelClass> List<S> save(Iterable<S> entities);

    @PostAuthorize("returnObject.id == principal.id")

    @Override
    MyHibernateModelClass findOne(Long integer);

    @PostFilter("filterObject.id == principal.id")

    @Override
    List<MyHibernateModelClass> findAll();
    
}

With this call, you implemented a JPA interface with ownership, where the CRUD calls can only be used by the user logged in the application is the same of the attribute id in the database table modeled with Hibernate MyHibernateModelClass.

I'm going to provide another example, which use a method of the hibernate entity that response with a boolean if the CRUD method can be use by this user.

Here is the next example:

(hibernate entity have this method):
...
public boolean hasMetadadaWithId(Long id) {
return (metadades.getId().equals(id));
}
...

And the JPA repository shows like this:

@Repository
public interface MyHibernateModelClassRepository extends JpaRepository<MyHibernateModelClass, Long> {

@PreAuthorize("#entity == null || #entity.hasMetadadaWithId(principal.id)")
    @Override
    <S extends MyHibernateModelClass> S save(S entity);

    @PreFilter("filterObject == null || filterObject.hasMetadadaWithId(principal.id)")

    @Override
    <S extends MyHibernateModelClass> List<S> save(Iterable<S> entities);
    
    @PostAuthorize("returnObject.hasMetadadaWithId(principal.id)")
    @Override
    MyHibernateModelClassfindOne(Long integer);

    @PostFilter("filterObject.hasMetadadaWithId(principal.id)")

    @Override
    List<MyHibernateModelClass> findAll();
    
}

And that's all!

For any question, ask me!

Try it and have fun!

martes, 10 de octubre de 2017

How to configure an unic properties file for Spring 4.3.3 application

Here you are!

Today I'm going to explain how to configure one or more properties files for Spring 4.3.3 application, with active profiles of Spring cofigured in the server on the run call.

You have to create a file, I named properties-context.xml with the next content:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<beans profile="DES">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:properties/file-DES.properties"/>
    </bean>
</beans>

<beans profile="INT">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:properties/file-INT.properties"/>
    </bean>
</beans>

<beans profile="PRO">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:properties/file-PRO.properties"/>
    </bean>
</beans>

</beans>

In this file, you can see that we use three profiles={DES,INT,PRO}

This configuration will use the last Spring configuration and will be always on the last version.

The, you have to import this file to the root-context.xml file or the main configuration file of Spring.

The line you have to add id this:

<!-- properties context -->
<import resource="properties-context.xml" />

Then, for load the diferents profiles, you have to call the server with the parameter: -Dspring.profiles.active=DES.

In this call, you can use more than one active profile and will load any profiles defined in the call.

For use more than one profile you can call them like this:
-Dspring.profiles.active=DES,OTHER_PROFILE.

Also, you can load any amount of properties files changing the value of the location parameter, like this:

<property name="location" value="classpath:properties/file-DES.properties,classpath:properties/other_file-DES.properties"/>

This is the way for load properties files in Spring 4.3.3.

Try it and have fun!