kamil@kamil-elitebook:~$ cat test.sh
#!/bin/bash
myvar=$(for i in {1..3}; do
echo $i
done)
echo "myvar=$myvar"
kamil@kamil-elitebook:~$ ./test.sh
myvar=1
2
3
sobota, 21 listopada 2015
How to assign for loop output to a variable in bash
sobota, 24 października 2015
AngularJS - form input custom directive = less code duplication
Each input part consist mainly of 3 parts:Name: <input type="text" ng-model="user.name" name="uName" required="" /> <br /> <div ng-show="form.$submitted || form.uName.$touched"> <div ng-show="form.uName.$error.required">Tell us your name.</div> </div> E-mail: <input type="email" ng-model="user.email" name="uEmail" required="" /> <div ng-show="form.$submitted || form.uEmail.$touched"> <span ng-show="form.uEmail.$error.required">Tell us your email.</span> <span ng-show="form.uEmail.$error.email">This is not a valid email.</span> </div>
- A label
- The actual input
- Validation error messages
It is not angular-specific, it is the way probably 99% of all the forms on the Internet are created. Now, let's imagine in our app we have like 20 forms with 10 input parts each. And on some beautiful day our Boss tells us that the labels should no more contain the ":" at the end. Hmmm sounds like 200 places to change the code :) sweet :) And what if they request us to do some more sophisticated modification - like display the validation messages only after user tries to submit the form? Sounds not only like 200 places to change the code, but also 200 inputs to check validation display logic, too :)
Yes, code duplication is horrible. Therefore, we should always transform self-contained concepts such as an input + label + validation message into an abstraction. E.g. like that one:
<my-input model="user.name" required="true" type="text" label="My label" name="name"></my-input>
This looks a bit nicer. The good news, it is actually possible with help of an angularjs custom directive. The final code looks as follows, the explanations for the most important parts are in the code comments:
'use strict';
/*
dependencies on:
- ngMessages to display validation error messages
- validation.match - this is angular-validation-match module which provides validation whether one field matches
value of another field, e.g. password vs confirm password
*/
angular.module('input', ['ngMessages', 'validation.match'])
/*
* the directive will be called meInput - we will use me prefix for the directives.
* Using a prefix for custom
* directives is recommended by angularJs developers. And of course don't use ng
* prefix as it is reserved for the built-in AngularJs directives.
*/
.directive('meInput', function() {
return {
/* template-building function is used. I don't use static template because of required DOM manipulations.
While AngularJs docs suggest compile and link functions to do the DOM manipulation, I wasn't able to
add custom input to the parent form - the input was in the HTML but was not added to the parent form's
controller
*/
template: buildTemplate,
//transclusion will make it possible to override the default error messages
transclude: true,
//the input should be wrapped in a form. And we're gonna need the form's controller to build input's id
require: '^form',
//link function - refer to the function itself for detailed description
link: link,
//we are creating isolated scope. @ means that we are adding the String literal provided as the directive
//attribute value and = creates a bidirectional binding between the parent scope property and the directive
//isolated scope's property. By bidirectional biding we can e.g. bind ng-Model in our directive to a parent
//scope property, so that we can read and update it
scope: {
name: '@',
model: '=',
label: '@',
required: '@',
meMatch: '=',
//it is not used as a scope value, but I specify it explicity for the sake of IDE validations and auto-complete
type: '@'
}
};
/* modifying the template DOM in compile function was not adding the input to the parent form. After some
* googling it turned out the template would need to be compiled against parent scope. Building the template
* string turned out to be a nicer solution
*
* The function 2nd parameter is of importance - it provides the values for attributes provided for the
* directive function. And values here mean the String literals, not actual value they point to in bidirectional
* (=) bindings in the parent scope. The bidirectional binding values will be populated only after the directive
* is compiled
*/
function buildTemplate(tElement, tAttributes) {
//{{::}} - one-time interpolation. The value for inputId is created in the link function, while label and
// name are provided as the directive's attribute.
var ret = '<label for="{{::inputId}}">{{::label}}:</label>\n' +
'<input id="{{::inputId}}" name="{{::name}}" ';
//if and only if meMatch attribute is specified the angular-validation-match directive will be added to the
//input
if (tAttributes.meMatch) {
ret += 'match="meMatch" ';
}
//type needs to be set once and for all due to issues in IE - we can't interpolate with e.g. {{::}}
ret+= 'type="' + tAttributes.type + '" ng-model="model" ng-required="required">\n' +
//now some magic with displaying the error messages, with some CSS classes to format the output.
//formCtrl is populated by the link function (see below)
' <div ng-show="(formCtrl[name].$touched || formCtrl.$submitted) && !formCtrl[name].$valid" ng-messages="formCtrl[name].$error" class="alert-box error">\n' +
//here the transclusion - as the transcluded content is above the default error messages, any error
//message transcluded will override the default ones. And, of course, we can add new messages as well
' <ng-transclude></ng-transclude>\n' +
' <div ng-message="email">Please provide a valid e-mail</div>\n' +
//this one is very nice - instead of providing a generic 'This field is required' we will display
//the actual field label in the required error message for even better usability!
'<div ng-message="required">{{::label}} is required</div>\n' +
'</div>';
return ret
}
// the link function does two important things:
function link(scope, iElement, iAttrs, controller){
//1. Provides access to the parent form controller so that we can check if the form has been submitted -
//this is used in the error messages validation logic
scope.formCtrl = controller;
//provides a unique id for the input. At least as long you don't have two forms with the same name on the
//same page. But you dont' do you?
scope.inputId = controller.$name + '-' + iAttrs.name;
}
});
In the above code I would point your attention especially to:
<div ng-message="required">{{::label}} is required</div>
With this nice trick we have e.g. Name is required message instead of ugly and generic This field is required. A nice usability feature showing that you actually care about your website and the users.
And most importantly, does this actually work? Well, here are the tests (in Jasmine):
'use strict';
describe('input directive', function() {
var compile;
var scope;
beforeEach(module('input'));
beforeEach(inject(function($compile, $rootScope) {
compile = $compile;
scope = $rootScope;
}));
it('should display error message when required and empty', function() {
//given
var testee = compile('<form name="testForm" >\n' +
' <me-input name="testName" type="email"\n' +
' model="user.test" required="true" label="label"></me-input>\n' +
'</form>')(scope);
//when
var input = testee.find('input');
input.triggerHandler('blur');
scope.$digest();
//then
expect(testee.html()).toContain('label is required');
});
it('should not display error message when not required', function() {
//given
var testee = compile('<form name="testForm">\n' +
' <me-input name="testName" type="email"\n' +
' model="user.test" label="label"></me-input>\n' +
'</form>')(scope);
//when
var input = testee.find('input');
input.triggerHandler('blur');
scope.$digest();
//then
expect(testee.html()).not.toContain('is required');
});
it('should override displayed error message with transcluded content', function() {
//given
var testee = compile('<form name="testForm">\n' +
' <me-input name="testName" type="email"\n' +
' model="user.test" required="true" label="label">' +
'<div ng-message="required">Custom required message</div> ' +
'</me-input>\n' +
'</form>')(scope);
//when
var input = testee.find('input');
input.triggerHandler('blur');
scope.$digest();
//then
expect(testee.html()).toContain('Custom required message');
});
it('should update model value when filled with proper data', function() {
//given
var testee = compile('<form name="testForm" >\n' +
' <me-input name="testName" type="email"\n' +
' model="modelTest" required="true" label="label"></me-input>\n' +
'</form>')(scope);
//when
testee.find('input').val('myemail@somedomain.com').triggerHandler('input');
scope.$apply();
//then
expect(scope.modelTest).toEqual('myemail@somedomain.com');
});
it('should contain the label with appropriate text', function() {
//given
var testee = compile('<form name="testForm" >\n' +
' <me-input name="testName" type="email"\n' +
' model="modelTest" required="true" label="label text"></me-input>\n' +
'</form>')(scope);
//when
var label = testee.find('label');
scope.$digest();
//then
expect(label.html()).toContain('label text');
});
describe('match directive integration', function() {
it('should display error if match is specified and value not matched', function() {
//given
var testee = compile('<form name="testForm" >\n' +
' <me-input name="matchMe" type="text"\n' +
' model="matchMe" label="label text"></me-input>\n' +
' <me-input name="testName" type="text"\n' +
' model="modelTest" label="label text" me-match="testForm.matchMe">' +
' <div ng-message="match">No match</div>\n\n' +
' </me-input>\n' +
'</form>')(scope);
//when
angular.element(testee.find('input')[1]).val('myemail@somedomain.com').triggerHandler('input');
scope.$apply();
//then
expect(testee.html()).toContain('No match');
});
});
});
sobota, 19 września 2015
How to touch input in AngularJS and Jasmine
describe("Touch a form input to make it display message", function() {
it('Should display message when touched', inject(function($compile, $rootScope) {
//given
var compiledFormHtml = $compile(
'<form name="testForm">\
<input name="testEmail" type="email" ng-model="user.email"/> \
<div ng-if="testForm.testEmail.$touched">\
I am touched\
</div>\
</form>')
($rootScope);
//when
var input = compiledFormHtml.find('input');
input.triggerHandler('blur');
$rootScope.$digest();
//then
expect(compiledFormHtml.html()).toContain('I am touched')
}));
it('Should not display the message when not touched', inject(function($compile, $rootScope) {
//given
var compiledFormHtml = $compile(
'<form name="testForm">\
<input name="testEmail" type="email" ng-model="user.email"/> \
<div ng-if="testForm.testEmail.$touched">\
I am touched\
</div>\
</form>')
($rootScope);
//when
$rootScope.$digest();
//then
expect(compiledFormHtml.html()).not.toContain('I am touched')
}));
});
Important things:- <form name="testForm"> name publishes the form's FormController into the scope.
- <input name="testEmail" type="email" ng-model="user.email"/> ng-model gives the input access to ngModelController, which again is published using the name attribute
- compiledFormHtml is a JQLite object having a subset of JQuery methods. With find we get reference to the input, which again is a JQLite object. Then we call the blur event with the triggerHandler method, which makes the input actually touched.
poniedziałek, 29 czerwca 2015
Quartz Scheduler + EJB Singleton = sure-fire (?) deadlock
@Singleton
@Startup
public class MyScheduler {
private Scheduler scheduler;
@PostConstruct
private void init() {
SchedulerFactory sf = new StdSchedulerFactory(props);
scheduler = sf.getScheduler();
scheduler.start();
}
public void scheduleJob(Class jobClass, Date executeAt) {
//build Trigger and JobDetail
//...
//and finally
scheduler.scheduleJob(jobDetail, trigger);
}
@PreDestroy
private void destructor() {
scheduler.shutdown();
}
}
While this design has many benefits:
- Automatic startup and shutdown of the scheduler with deployment and undeployment of the application
- Serializing multithreaded access to Quartz scheduler (although some developer on terracota forum claims the scheduler to be thread-safe there is no mention of it in the Scheduler javadoc and we prefer not to debug potential multi-threading issues in a 3rd party library
- Using the singleton pattern we have a single Quartz Scheduler instance in the application
- invoke ejbA.someMethod()
- ejbA.someMethod() starts a new transaction
- within ejbA.someMethod you schedule some job via MyScheduler.scheduleJob(myJobClass, myJobExecuteAt). Quartz Scheduler acquirers its internal lock on trigger access in the DB (something like SELECT * FOR UPDATE FROM QUARTZ_LOCKS WHERE LOCK_NAME = 'TRIGGER_ACCESS' AND SCHED_NAME = 'yourSchedulerName')
- in parallel, somewhere in the same application ejbB.someOtherMethod() gets invoked
- within ejbB.someOtherMethod() you want to schedule some other job via MyScheduler.scheduleJob(myOtherJobClass, otherJobExecuteAt). As Quartz is again trying to get its lock on trigger access in the DB and the ejbA.someMethod() transaction has neither rolled back nor commited, it is stuck waiting for the DB lock.
- Now, within ejbA.someMethod() you want to schedule another job via MyScheduler.scheduleJob(yetAnotherJobClass, yetAnotherJobExecuteAt). Unfortunately, in accordance with the EJB Singleton multithreading semantics the ejbB.someOtherMethod() acquired the lock to our singleton MyScheduler and ejbA.someMethod() has to wait until ejbB.someOtherMethod() exits from MyScheduler.scheduleJob(myOtherJobClass, otherJobExecuteAt) invocation.
And now we have a classic deadlock - ejbA.someMethod() is holding lock to trigger access while waiting for lock to MyScheduler and ejbB.someOtherMethod() is holding lock to MyScheduler and is wating for lock to trigger access.
In fact, a similar deadlock scenario can happen in any EJB Singleton with serialized access which creates DB locks (not only explicit locks but also e.g. implicit locks for DML updates or inserts)
Solution
The solution is much less complicated than the deadlock scenario:- either schedule each Job in separate transaction (e.g. annotate the MyScheduler.scheduleJob() method with @TransactionAttribute(REQUIRES_NEW); this has the disadvantage that you lose the atomicity of the business logic that you are executing and scheduling of the job)
-
or schedule all the jobs you need in single invocation of MyScheduler via method like MyScheduler.scheduleJobs(List
jobsToSchedule>) where MyJobData class contains all the information you need to schedule a single job (i.e. job class, job execution date time, possibly also job params)
piątek, 26 czerwca 2015
Docker & Gentoo Linux & su could not open session
kamil@kamil-elitebook:~$ docker run -ti oraclelinux:6 /bin/bash [root@bac7e6cd1aad /]# su - oracle could not open session2 hours of googling later:
kamil@kamil-elitebook:~$ docker run --privileged -ti oraclelinux:6 /bin/bash [root@2f676eeb3b7a /]# su - oracle [oracle@2f676eeb3b7a ~]$
sobota, 21 lutego 2015
JBoss dummy login module with predefined role
In case you need to test authorization of your JBoss-deployed application and you don't want to connect to the remote system or can't configure it to return proper roles for you, you may use JBoss Identity login module:
<security-domain name="mydomain" cache-type="default">
<authentication>
<login-module code="Identity" flag="required">
<module-option name="principal" value="myprincipal"/>
<module-option name="roles" value="ROLE_TO_BE_TESTED1,ROLE_TO_BE_TESTED2"/>
</login-module>
</authentication>
</security-domain>
where:- mydomain is the security domain name specified in the application
- myprincipal - principal (i.e. login) name of the authenticated user. Default is guest.
- ROLE_TO_BE_TESTED1,ROLE_TO_BE_TESTED2 - comma-delimited list of roles that will be assigned to the authenticated user.
sobota, 24 stycznia 2015
Authorizing JBoss EAP 6.1 Alpha web app users with Active Directory
After some research it turned out 2 options were available:
- Kerberos/SPNEGO-based SSO.
- Authentication using LDAP protocol.
Now, what I needed to do was to configure the JBoss security domain and the ldap authentication module in standalone.xml:
<security-domain name="LDAPRealm">
<authentication>
<login-module code="Ldap" flag="required">
<module-option name="java.naming.provider.url" value="ldap://hostname:389/"/>
<module-option name="java.naming.security.authentication" value="DIGEST-MD5"/>
<module-option name="javax.security.sasl.qop" value="auth-conf"/>
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
<!-- Database module to retrieve user roles - will be described in another post -->
</authentication>
</security-domain>
As for the meaning of each option:java.naming.provider.url - the URL of ldap one wants to connect to. It is important - at least using DIGEST-MD5 - that it is set to hostname, which the Active Directory server recognizes. In particular the IP address shouldn't be used. Otherwise one would get:
javax.naming.AuthenticationException: [LDAP: error code 49 - 80090303: LdapErr: DSID-0C090420, comment: The digest-uri does not match any LDAP SPN's registered for this server., data 0, vecejava.naming.security.authentication - type of authentication to use:
- none - no authentication. That's not what I want - I do want to authenticate the user
- simple - plain text authentication using Distinguished Name as username, e.g. in my case this would be CN=Kamil Roman,CN=Users,DC=mydomain,DC=com. Please note that the domain username is not used in the login string.
- one (or more) SASL authentication mechanism. For most LDAPs including Active Directory you can check the supported mechanisms by issuing
kamil@localhost:~$ ldapsearch -H ldap://192.168.1.100 -x -b "" -s base -LLL supportedSASLMechanisms dn: supportedSASLMechanisms: GSSAPI supportedSASLMechanisms: GSS-SPNEGO supportedSASLMechanisms: EXTERNAL supportedSASLMechanisms: DIGEST-MD5as long as DIGEST-MD5 is returned on the list it is fine
In case of SASL authentication the domain login (e.g. kroman in my case) is used as username.
javax.security.sasl.qop - security level of the protocol, valid options:
- auth - Authentication only (default)
- auth-int - Authentication with integrity protection
- auth-conf - Authentication with integrity and privacy protection
0002028: LdapErr: DSID-0C090169, comment: The server requires binds to turn on integrity checking if SSL\TLS are not already active on the connection, data 0, veceAnyway, using auth-conf seems the most secure option and thus the best as long as it is supported by the LDAP server.
password-stacking - setting this to useFirstPass will result in retrieving roles from the next login-module (will be described in another post)
That's it on the app server side! Now we need to specify to use the newly configured security domain in the WEB-INF/jboss-web.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>java:/jaas/LDAPRealm</security-domain>
</jboss-web>
Troubleshooting
- set logging level on org.jboss.security and org.jboss.as.web.security to TRACE or ALL
- JBoss swallows the root cause authentication exception and in the log file you will always get only
DEBUG [org.jboss.security] (http-/0.0.0.0:8080-5) PBOX000206: Login failure: javax.security.auth.login.FailedLoginException: PBOX000070: Password invalid/Password required
and ajavax.servlet.ServletException: JBWEB000053: Failed to authenticate a principal thrown from the HttpServletRequest.login() if you use this method for authentication.
In order to know the root cause and/or message received from the AD server you need to debug the LdapLoginModule.validatePassword method and especially its catch(Throwable e){} block