tag:blogger.com,1999:blog-57080609781158655772024-03-21T06:18:28.892-07:00P1CoderP1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-5708060978115865577.post-12697148179544731632020-12-31T18:23:00.004-08:002020-12-31T18:36:57.437-08:00Image compression using C#<p>Sometimes, we need to compress the image files while maintaining the image quality. This can be achieved by the following C# implementation.</p><p>Usage: zoomImage(path, zoom).Save(destImagePath);</p><p> private Image zoomImage(string fileName, float zoom = 0.25f) // set quality to 1-100, eg 50</p><p> {</p><p> Image img = Image.FromFile(fileName);</p><p> Image zoomedImage = new Bitmap(Image.FromFile(fileName), Convert.ToInt32(img.Width * zoom), Convert.ToInt32(img.Height * zoom));</p><p> int quality = 100;</p><p> var myImageCodecInfo = ImageCodecInfo.GetImageEncoders().Where(encoder => encoder.MimeType == "image/jpeg").FirstOrDefault();</p><p> var myEncoderParameters = new EncoderParameters(1);</p><p> myEncoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);</p><p><br /></p><p> var stream = new MemoryStream();</p><p> zoomedImage.Save(stream, myImageCodecInfo, myEncoderParameters);</p><p> var newImage = Image.FromStream(stream);</p><p> var g = Graphics.FromImage(newImage);</p><p> g.InterpolationMode = InterpolationMode.Default;</p><p> g.DrawImage(newImage, new Rectangle(Point.Empty, newImage.Size), 0, 0,</p><p> newImage.Width, newImage.Height, GraphicsUnit.Pixel, new ImageAttributes());</p><p> return newImage;</p><p> }</p>P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-62361933218823095392020-12-31T18:19:00.003-08:002020-12-31T18:23:40.936-08:00MySQL partitioningP1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-72180749604546750742020-12-30T19:51:00.003-08:002020-12-31T18:24:17.260-08:00JWT & Spring BootP1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-28919019140083432632019-07-31T20:56:00.002-07:002020-02-24T18:13:02.328-08:00How to solve the windows update hanging at "Gear" icon in Windows10?<div dir="ltr" style="text-align: left;" trbidi="on">
Windows 10 needs to have windows update installed regularly.<br />
Sometimes, updates are not getting downloaded without visible reasons.<br />
One important service for Windows Update is "Windows Update Medic Service".<br />
<br />
<br />
<ul style="text-align: left;">
<li>Open the Registry Editor by executing regedit command.</li>
<li>Go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc in registry editor.</li>
<li>Double-click on Start registry DWORD to modify its Value data in right pane.</li>
<li>Set the Value data to 4 to disable Windows Update Medic Service (1 to enable that service)</li>
<li>Click "OK". Close Registry Editor and restart the machine to make changes effective.</li>
</ul>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-63364360603156360482019-07-01T20:28:00.000-07:002019-07-08T18:03:09.330-07:00How to keep track the changes to JPA in Spring Boot?<div dir="ltr" style="text-align: left;" trbidi="on">
We develop the LOB software of different domain and almost every app use backend database to persist the data. In Java, <b>JPA </b>is widely use for <i>ORM </i>layer in between of app and database. In many occasions, CRUD <b>logging </b>is part of the basic requirement.<br />
One common scenario is application needs to keep track of price change on a particular stock item within a year. It start with $25 initially and increase to $30 in a month. After 3 months, it goes down a little bit to $28 due to excess supply. In following month, it goes back to $30 thanks to higher demand and etc.<br />
Most probably, this kind of information is useful for the end-users and our application needs to support this.<br />
Of course, we can implement this feature by hard-coding each updates to the database, can't we ???<br />
But, we have better option with <b>JPA Entity Listeners</b>. We can easily bind the event listener and persist each changes to database. It is simple and easy to implement with the help of <i>Spring Boot</i>.<br />
Create a new Spring Boot application. Assume we have the following in <b>application.properties</b> file.<hr />
spring.datasource.url = jdbc:mysql://localhost:3306/jpa-logger?useSSL=false<br />
spring.datasource.username = test<br />
spring.datasource.password = test<br />
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect<br />
spring.jpa.hibernate.ddl-auto = create<hr />
Detail implementation is as followed.<br />
<details>
<summary>This is REST controller which is for testing JPA logging feature</summary>
<script src="https://gist.github.com/p1coderblog/423edd29229b5111464c0008c8c48376.js"></script>
</details>
<br />
<details>
<summary>AutowireUtil class is used to auto-wire spring-beans inside JPA listener</summary>
<script src="https://gist.github.com/p1coderblog/7f54f8cb6ac036473bd368be9f29929c.js"></script>
</details>
<br />
<details>
<summary>This is the base class for all model which require the JPA logging for CRUD action.</summary>
<script src="https://gist.github.com/p1coderblog/fffc208c5736ad00497a67eb811f82b2.js"></script>
</details>
<br />
<details>
<summary>This is Employee class which inherit from BaseModel.</summary>
<script src="https://gist.github.com/p1coderblog/cbcfcafb007adf17ce975f94325b2f22.js"></script>
</details>
<br />
<details>
<summary>This is the meta information of CRUD event to be logged into database.</summary>
<script src="https://gist.github.com/p1coderblog/683c18bf6afce371c30da244934ebbcb.js"></script>
</details>
<br />
<details>
<summary>This is the listener class which persist the CRUD meta information into database.</summary>
<script src="https://gist.github.com/p1coderblog/85555f7a735d4b2a8c326ddb106dec1b.js"></script>
</details>
<br />
<details>
<summary>This class inject auto-wire the bean for listener.</summary>
<script src="https://gist.github.com/p1coderblog/4a6ab5db090ee37c274a3ae74e9a05d9.js"></script>
</details>
<br />
<details>
<summary>EmployeeRepository.java</summary>
<script src="https://gist.github.com/p1coderblog/3a1d552691cb9afdf23b8aa6235cac83.js"></script>
</details>
<br />
<details>
<summary>EventRepository.java</summary>
<script src="https://gist.github.com/p1coderblog/511708f0fb6ea0862337dbf1e8ef7271.js"></script>
</details>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com3tag:blogger.com,1999:blog-5708060978115865577.post-13320907870564126202019-04-27T17:07:00.004-07:002019-04-28T07:20:09.477-07:00How to enable image resizing inside contenteditable DIV (aka WYSIWYG editor) in Chrome?<div dir="ltr" style="text-align: left;" trbidi="on">
When we put the image inside contenteditable DIV (aka WYSIWYG editor), IE allow resizing by default but Chrome does not. In fact, we can easily implement this features using <i>pure Javascript</i>. I compile DOM handling to mouse, mouseup and other event handler into a sample program.
<details>
<summary>Source code</summary>
<script src="https://gist.github.com/p1coderblog/24cdae9fa923193f45f3583c2265161d.js"></script>
</details>
<div>
<style type="text/css">
#edt {
padding: 2px;
width: 100%;
height: 500px;
border: 1px solid black;
overflow: scroll;
}
</style>
<div id="edt" contenteditable="true">
<p><b>Green</b><img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAABeCAYAAADi4bzuAAABh0lEQVR4Ae3SMRXAIBAFQYKV6InYKISGgvUwV91v983z/t8aToFTYCqhwF0AiLuGfwABQQoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykABDJYQDBQAoAkRwGEAykwAbutgLcDSwO9gAAAABJRU5ErkJggg==">Red
<img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIQAAABeCAYAAADi4bzuAAAEQElEQVR4Ae2cPXLcMAyFn0TJXk8mXVKlzY1S5ghpcy3fKCdIxl1+7JVIZsAV1oJ3xg3cEPuYWfNHQsZ47xNIbeHh4dPnCjYqsCkwUgkqsFeAQOzV4BgEghAYBQiEkYMTAkEGjAIEwsjBCYEgA0YBAmHk4IRAkAGjAIEwcnBCIMiAUYBAGDk4IRBkwChAIIwcnBAIMmAUIBBGDk4IBBkwChAIIwcnBIIMGAUIhJGDEwJBBowCBMLIwQmBIANGAQJh5OCEQJABowCBMHJwQiDIgFGAQBg5OCEQZMAoQCCMHJyEB6IMgHy07Ya6xH6nwLQbdzncmz2++EsXcq0qARWQodwj63lbfxnTpQhv+Et3XSHUcDFdjBWP1X+99oZaXcV/1X2FEJeGCiQBop4qgjz9AkkDpTzDIveyMrzOddcVQmFo1UErxNbrtdZvW4VWEVmTCiIfNqtA1xVC93/dKnSOAshxQg3Xo4VUEHkC9ttJ2WCxslzvrGsg1DY1X4zWbUMgEUBkra3LXAO2beZ84NytX/uwayDEaGllBJ62sZ4nkjz6WxPj1w0WOWtIk75q6djuY2cfmi71ELPVewFE5m1tA0SSEki07YbnNxK9xr5zINohsY44jDdIjwW3T8D78YC6nGwfptS2DakGUjF0C9E3DQJwqUDXW0ZLJxfUsuLdfIthGPD49x8O84xjXZBzRtpy3hWMtrKvFJeyXO9K90BM04S8rMgVrU9TAkrBKP+GERXZbA1aJa7X8tcz7x6IY14x3844lgJMM9ZaMaWEYc3nzLUayBlDgJCPNKkaeu1885UP9m9i3UnRXidvZ/xGwUNa8OH7N3z8+gV/UsFSC+r2GpFH4JiAJQEyljaV07ni5VbSnQhv/At3DYRosSwZa8443N0Bv37ix/096jBgupnPQMh98uax72VMGE6a7H8OPf81fKkQaTpgQcG6HjHK+UFaqcCSkcYRadsU9JVULut3FXqe4LZxkk1+dn2GEEPX5elkfBmQ8tDeLOoopKT21oFyOlS2c8P2zaUkLhA0SJ614Kh3IMTBVgFKbvW/lBVD+0Zye+Zt1wzXA6VMWBkun4HuzxCXKXHFowCB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVkxKB8KgXMJZABDTVk9J/bM/jwqGSC8IAAAAASUVORK5CYII=">
</p>
</div>
<script type="text/javascript">
function enableImageResizeInDiv(id) {
if (!(/chrome/i.test(navigator.userAgent) && /google/i.test(window.navigator.vendor))) {
return;
}
var editor = document.getElementById(id);
var resizing = false;
var currentImage;
var createDOM = function (elementType, className, styles) {
let ele = document.createElement(elementType);
ele.className = className;
setStyle(ele, styles);
return ele;
};
var setStyle = function (ele, styles) {
for (key in styles) {
ele.style[key] = styles[key];
}
return ele;
};
var removeResizeFrame = function () {
document.querySelectorAll(".resize-frame,.resizer").forEach((item) => item.parentNode.removeChild(item));
};
var offset = function offset(el) {
const rect = el.getBoundingClientRect(),
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return { top: rect.top + scrollTop, left: rect.left + scrollLeft }
};
var clickImage = function (img) {
removeResizeFrame();
currentImage = img;
const imgHeight = img.offsetHeight;
const imgWidth = img.offsetWidth;
const imgPosition = { top: img.offsetTop, left: img.offsetLeft };
const editorScrollTop = editor.scrollTop;
const editorScrollLeft = editor.scrollLeft;
const top = imgPosition.top - editorScrollTop - 1;
const left = imgPosition.left - editorScrollLeft - 1;
editor.append(createDOM('span', 'resize-frame', {
margin: '10px',
position: 'absolute',
top: (top + imgHeight - 10) + 'px',
left: (left + imgWidth - 10) + 'px',
border: 'solid 3px blue',
width: '6px',
height: '6px',
cursor: 'se-resize',
zIndex: 1
}));
editor.append(createDOM('span', 'resizer top-border', {
position: 'absolute',
top: (top) + 'px',
left: (left) + 'px',
border: 'dashed 1px grey',
width: imgWidth + 'px',
height: '0px'
}));
editor.append(createDOM('span', 'resizer left-border', {
position: 'absolute',
top: (top) + 'px',
left: (left) + 'px',
border: 'dashed 1px grey',
width: '0px',
height: imgHeight + 'px'
}));
editor.append(createDOM('span', 'resizer right-border', {
position: 'absolute',
top: (top) + 'px',
left: (left + imgWidth) + 'px',
border: 'dashed 1px grey',
width: '0px',
height: imgHeight + 'px'
}));
editor.append(createDOM('span', 'resizer bottom-border', {
position: 'absolute',
top: (top + imgHeight) + 'px',
left: (left) + 'px',
border: 'dashed 1px grey',
width: imgWidth + 'px',
height: '0px'
}));
document.querySelector('.resize-frame').onmousedown = () => {
resizing = true;
return false;
};
editor.onmouseup = () => {
if (resizing) {
currentImage.style.width = document.querySelector('.top-border').offsetWidth + 'px';
currentImage.style.height = document.querySelector('.left-border').offsetHeight + 'px';
refresh();
currentImage.click();
resizing = false;
}
};
editor.onmousemove = (e) => {
if (currentImage && resizing) {
let height = e.pageY - offset(currentImage).top;
let width = e.pageX - offset(currentImage).left;
height = height < 1 ? 1 : height;
width = width < 1 ? 1 : width;
console.log({'e.Y': e.pageY, 'i.t': imgPosition.top, 'i.ot': img.offsetTop, 'e.sT': editor.scrollTop, height: height, 'e.X': e.pageX, 'i.left': imgPosition.left, 'e.sL': editor.scrollLeft, width: width});
const top = imgPosition.top - editorScrollTop - 1;
const left = imgPosition.left - editorScrollLeft - 1;
setStyle(document.querySelector('.resize-frame'), {
top: (top + height - 10) + 'px',
left: (left + width - 10) + "px"
});
setStyle(document.querySelector('.top-border'), { width: width + "px" });
setStyle(document.querySelector('.left-border'), { height: height + "px" });
setStyle(document.querySelector('.right-border'), {
left: (left + width) + 'px',
height: height + "px"
});
setStyle(document.querySelector('.bottom-border'), {
top: (top + height) + 'px',
width: width + "px"
});
}
return false;
};
};
var bindClickListener = function () {
editor.querySelectorAll('img').forEach((img, i) => {
img.onclick = (e) => {
if (e.target === img) {
clickImage(img);
}
};
});
};
var refresh = function () {
bindClickListener();
removeResizeFrame();
if (!currentImage) {
return;
}
var img = currentImage;
var imgHeight = img.offsetHeight;
var imgWidth = img.offsetWidth;
var imgPosition = { top: img.offsetTop, left: img.offsetLeft };
var editorScrollTop = editor.scrollTop;
var editorScrollLeft = editor.scrollLeft;
const top = imgPosition.top - editorScrollTop - 1;
const left = imgPosition.left - editorScrollLeft - 1;
editor.append(createDOM('span', 'resize-frame', {
position: 'absolute',
top: (top + imgHeight) + 'px',
left: (left + imgWidth) + 'px',
border: 'solid 2px red',
width: '6px',
height: '6px',
cursor: 'se-resize',
zIndex: 1
}));
editor.append(createDOM('span', 'resizer', {
position: 'absolute',
top: (top) + 'px',
left: (left) + 'px',
border: 'dashed 1px grey',
width: imgWidth + 'px',
height: '0px'
}));
editor.append(createDOM('span', 'resizer', {
position: 'absolute',
top: (top) + 'px',
left: (left + imgWidth) + 'px',
border: 'dashed 1px grey',
width: '0px',
height: imgHeight + 'px'
}));
editor.append(createDOM('span', 'resizer', {
position: 'absolute',
top: (top + imgHeight) + 'px',
left: (left) + 'px',
border: 'dashed 1px grey',
width: imgWidth + 'px',
height: '0px'
}));
};
var reset = function () {
if (currentImage != null) {
currentImage = null;
resizing = false;
removeResizeFrame();
}
bindClickListener();
};
editor.addEventListener('scroll', function () {
reset();
}, false);
editor.addEventListener('mouseup', function (e) {
if (!resizing) {
const x = (e.x) ? e.x : e.clientX;
const y = (e.y) ? e.y : e.clientY;
let mouseUpElement = document.elementFromPoint(x, y);
if (mouseUpElement) {
let matchingElement = null;
if (mouseUpElement.tagName === 'IMG') {
matchingElement = mouseUpElement;
}
if (!matchingElement) {
reset();
} else {
clickImage(matchingElement);
}
}
}
});
}
enableImageResizeInDiv('edt');
</script>
</div>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com5tag:blogger.com,1999:blog-5708060978115865577.post-84335861830404821382019-04-14T14:44:00.000-07:002019-04-14T14:55:52.104-07:00Paste the image into contenteditable div<div dir="ltr" style="text-align: left;" trbidi="on">
When we accept the user input data, user input text data most of the time. But for some application, users need to input image and website needs to persist them. With the support of <b>FileReader</b> and <b>clipboardData</b> of <i>paste</i> event, we can easily enable that direct image input. User can directly copy the images from the software like MS-Paint or Web Browser and paste it into our input control (<b>contenteditable</b> <b>div</b> in our scenario).<br /><br />
<details>
<summary>Sample Text Editor with Image copy-paste support</summary>
<script src="https://gist.github.com/p1coderblog/8661ac811f613af1503929d3cc9a26d7.js"></script>
</details>
<style type="text/css">
#edt {
padding: 2px;
width: 100%;
height: 500px;
border: 1px solid black;
overflow: scroll;
}
</style>
<input onchange="setDocMode(this.checked);" type="checkbox" />Show HTML source
<br />
<div contenteditable="true" id="edt">
<b>Sample</b><img src="data:image/png;base64,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" /><i>Image</i>
</div>
<script type="text/javascript">
let edt = document.getElementById("edt");
function setDocMode(bToSource) {
let oContent;
if (bToSource) {
oContent = document.createTextNode(edt.innerHTML);
edt.innerHTML = "";
edt.contentEditable = false;
let oPre = document.createElement("pre");
oPre.id = "sourceView";
oPre.contentEditable = true;
oPre.appendChild(oContent);
edt.appendChild(oPre);
document.execCommand("defaultParagraphSeparator", false, "div");
} else {
if (document.all) {
edt.innerHTML = edt.innerText;
} else {
oContent = document.createRange();
oContent.selectNodeContents(edt.firstChild);
edt.innerHTML = oContent.toString();
}
edt.contentEditable = true;
}
edt.focus();
}
document.getElementById('edt').addEventListener("paste", (event) => {
var clipboardData = event.clipboardData;
clipboardData.types.forEach((type, i) => {
const fileType = clipboardData.items[i].type;
if (fileType.match(/image.*/)) {
const file = clipboardData.items[i].getAsFile();
const reader = new FileReader();
reader.onload = function (evt) {
const dataURL = evt.target.result;
const img = document.createElement("img");
img.src = dataURL;
document.execCommand('insertHTML', true, img.outerHTML);
};
reader.readAsDataURL(file);
}
});
});
</script>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com1tag:blogger.com,1999:blog-5708060978115865577.post-37503121409064261012019-03-10T11:27:00.001-07:002019-03-10T11:28:25.371-07:00Race condition in multi-threaded application<div dir="ltr" style="text-align: left;" trbidi="on">
When we develop the multi-threaded application, it's critical to ensure that no <b>race condition</b> occurs. Otherwise, our program will produce the inconsistent result.<br />
Let see the demo program. When we execute this program for 5 times, it will output 1000 for each run.<br />
public class RaceCondition implements Runnable {<br />
<span style="white-space: pre;"> </span>static Object lock = new Object();<br />
<span style="white-space: pre;"> </span>static int sharedNumber = 0;<br />
<br />
<span style="white-space: pre;"> </span>public static void main(String[] args) {<br />
<span style="white-space: pre;"> </span>Thread t1 = new Thread(new RaceCondition());<br />
<span style="white-space: pre;"> </span>Thread t2 = new Thread(new RaceCondition());<br />
<span style="white-space: pre;"> </span>t1.start();<br />
<span style="white-space: pre;"> </span>t2.start();<br />
<span style="white-space: pre;"> </span>try {<br />
<span style="white-space: pre;"> </span>t1.join();<br />
<span style="white-space: pre;"> </span>t2.join();<br />
<span style="white-space: pre;"> </span>} catch (Exception e) {<br />
<span style="white-space: pre;"> </span>e.printStackTrace();<br />
<span style="white-space: pre;"> </span>}<br />
<span style="white-space: pre;"> </span>System.out.println(sharedNumber);<br />
<span style="white-space: pre;"> </span>}<br />
<br />
<span style="white-space: pre;"> </span>public void run() {<br />
<span style="white-space: pre;"> </span>boolean wait = true;<br />
<span style="white-space: pre;"> </span>for (int i = 0; i < 500; i++) {<br />
<span style="white-space: pre;"> </span>synchronized (lock) {<br />
<span style="white-space: pre;"> </span>int buffer = sharedNumber;<br />
<span style="white-space: pre;"> </span>buffer++;<br />
<span style="white-space: pre;"> </span>if (Math.random() > 0.5 && wait) {<br />
<span style="white-space: pre;"> </span>wait = false;<br />
<span style="white-space: pre;"> </span>try {<br />
<span style="white-space: pre;"> </span>Thread.sleep(1000);<br />
<span style="white-space: pre;"> </span>} catch (InterruptedException e) {<br />
<span style="white-space: pre;"> </span>e.printStackTrace();<br />
<span style="white-space: pre;"> </span>}<br />
<span style="white-space: pre;"> </span>}<br />
<span style="white-space: pre;"> </span>sharedNumber = buffer;<br />
<span style="white-space: pre;"> </span>}<br />
<span style="white-space: pre;"> </span>}<br />
<span style="white-space: pre;"> </span>}<br />
}<br />
Ok, let's comment "<b>synchronized</b>" code line and it's closing curly brace line. This time, we will have 5 different values after executing for 5 times.<br />
<b>synchronized </b>block ensure that only one thread can execute on the share/same object at a time in its block. It prevent the possible <b>race condition</b>.</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-26241190769333086512019-03-05T15:16:00.002-08:002019-03-05T17:32:06.345-08:00How to implement CRUD features in ASP.NET Web API?<div dir="ltr" style="text-align: left;" trbidi="on">
We are going to discuss how we can implement RESTful service layer using ASP.NET Web API.<br />
In this program, we will use session object to persist as in-memory datastore.<br />
Yes, REST is <strong>stateless</strong>. By using session, we will make it <i>stateful</i>.
Well, the only purpose of using session in this program is to use some kind of back-end data store when we do CRUD actions.
We are not using this type of data store in production code. We will use industry grade databases for that.<br />
Alright, let's start hands-on work now.
Open up Visual Studio 2015 and select File => New => Project and select "ASP.NET Web Application (.NET Framework)". Click "WebAPI" checkbox and create new project.<br />
First we will create the Model class for Employee entity.
<br />
<details>
<summary>Model class</summary>
<script src="https://gist.github.com/p1coderblog/d2631f2229596a71b6ce5f3b1c26539e.js"></script>
</details>
And make sure our WebApi return data in json format.
<br />
<details>
<summary>Configure to return JSON format</summary>
<script src="https://gist.github.com/p1coderblog/c214f671230e4bfeb816b4cfcafe6372.js"></script>
</details>
Here we will implement the API controller logic
<br />
<details>
<summary>Implement the RESTful API in the controller</summary>
<script src="https://gist.github.com/p1coderblog/e2e17743b9f98f6946d20ee9f153a1a2.js"></script>
</details>
In real-word application, we will have back-end database with ORM tool like Entity Framework. We will use dummy data for demo.
<br />
<details>
<summary>Generate the sample data in ActionFilter</summary>
<script src="https://gist.github.com/p1coderblog/275f452c266860aa1bef3252c94bd48c.js"></script>
</details>
For any WebApi, security is a very important factor. For simplicity sake, I just basic authentication to show-case the usage pattern.
<br />
<details>
<summary>For authenticating the routes</summary>
<script src="https://gist.github.com/p1coderblog/1820d3bdbaa3eb21bf89bd38be14f1e8.js"></script>
</details>
Add the entry for authentication in web.config file.
<br />
<system.webServer> <modules> <add name="BasicAuthHttpModule" type="WebApiSampleProject.BasicAuthHttpModule, WebApiSampleProject" /> </modules> </system.webServer>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com1tag:blogger.com,1999:blog-5708060978115865577.post-79883950858923780712019-02-23T16:56:00.001-08:002019-02-23T18:00:04.088-08:00Convert the tab separated data into grid-style data<div dir="ltr" style="text-align: left;" trbidi="on">
<style>
.monospaced {
font-family: Courier;
font-size: 15px;
width: 800px;
}
</style>
We use different kinds of text layout styles, like Comma-delimited, tab-delimited, etc. Some of the customers give feedback about they find it more intuitive to see the data in <strong>simple grid layout</strong> for some tabular format data.<br /> This program is simplified version of what we can do to convert tab-delimited data into grid layout table-style data with simple pure JavaScript.
<details>
<summary>SourceCode</summary>
<script src="https://gist.github.com/p1coderblog/4ddc3c90c21451c65e38d85bbd92f905.js"></script>
</details>
<textarea class="monospaced" cols="60" id="rawdata" rows="5"> </textarea>
<br />
<input id="convert" type="button" value="Convert Raw Data to Table-style data" />
<br />
<hr>
<div class="monospaced" id="result">
</div>
<script>
document.getElementById('rawdata').innerHTML = "x\tfile1\tfile2\tfile2\tfile2\n"+
"bob\tread,write,own\t\tread\tread,write,execute,own\n"+
"jill\tappend\tappend\tread\tread,write,execute,own";
document.getElementById('convert').addEventListener("click", () => {
document.getElementById('result').innerHTML = "";
const raw = document.getElementById('rawdata').value;
const lines = raw.split(/\n/);
lines.forEach((line) => {
const cols = line.split("\t");
let text = '';
cols.forEach((word, index) => {
if (index == cols.length - 1) {
text += word;
} else {
let update = word.padEnd(15, "'").concat('|');
update = update.replace(/'/g, " ");
text += update;
}
});
document.getElementById('result').innerHTML += text.concat('<br>');
});
});
</script>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com0tag:blogger.com,1999:blog-5708060978115865577.post-9203548357265920032019-01-20T18:54:00.002-08:002019-01-22T18:38:57.411-08:00Database Normalization<div dir="ltr" style="text-align: left;" trbidi="on">
<style>
.tbl {
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
border-collapse: collapse;
width: 100%;
}
.tbl td,
customers th {
border: 1px solid #ddd;
padding: 1px;
}
.tbl tr:nth-child(even) {
background-color: #f2f2f2;
}
.tbl tr:hover {
background-color: #ddd;
}
.tbl th {
padding-top: 12px;
padding-bottom: 12px;
text-align: left;
background-color: #4CAF50;
color: white;
}
</style>
What is Normalization?<br />
Normalzation is a database design technique that reduces the data redundancy and dependency.<br />
Let's start to see how we do normalization in sample data of football player information. <br />
We will see how we procced from Un-normalized Form to First, Second, Third Normal Form step-by step.<br />
<br />
Initially, our data is in un-normalized form. It contains composite or multiple values in an attribute (Clubs).
<br />
<table class="tbl">
<tbody>
<tr>
<th>Player Name</th>
<th style="width: 40%;">Clubs</th>
<th>Agent</th>
<th>Position</th>
</tr>
<tr>
<td>Ronaldo</td>
<td>Juventus, Real Madrid, Manchester United, Sporting Lisbon</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>Rooney</td>
<td>DC United, Manchester United, Everton</td>
<td>David</td>
<td>Forward</td>
</tr>
<tr>
<td>Nani</td>
<td>Manchester United, Sporting Lisbon</td>
<td>Mandez</td>
<td>Winger</td>
</tr>
</tbody></table>
<br />
<hr />
<br />
<strong>1st Normal Form</strong><br />
Each column should contain a single value and each record have to be unique.
<br />
<table class="tbl">
<tbody>
<tr>
<th>Player Name</th>
<th>Club</th>
<th>Agent</th>
<th>Position</th>
</tr>
<tr>
<td>Ronaldo</td>
<td>Juventus</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>Ronaldo</td>
<td>Real Madrid</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>Ronaldo</td>
<td>Manchester United</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>Ronaldo</td>
<td>Sporting Lisbon</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>Rooney</td>
<td>DC United</td>
<td>David</td>
<td>Forward</td>
</tr>
<tr>
<td>Rooney</td>
<td>Manchester United</td>
<td>David</td>
<td>Forward</td>
</tr>
<tr>
<td>Rooney</td>
<td>Everton</td>
<td>David</td>
<td>Forward</td>
</tr>
<tr>
<td>Nani</td>
<td>Manchester United</td>
<td>Mandez</td>
<td>Winger</td>
</tr>
<tr>
<td>Nani</td>
<td>Sporting Lisbon</td>
<td>Mandez</td>
<td>Winger</td>
</tr>
</tbody></table>
<br />
<hr />
<br />
<strong>2nd Normal Form</strong><br />
Now, we are in 1NF. To make it 2NF, we need to define primary key.
<br />
<table class="tbl">
<tbody>
<tr>
<th>Player Id</th>
<th>Player Name</th>
<th>Agent</th>
<th>Position</th>
</tr>
<tr>
<td>1</td>
<td>Ronaldo</td>
<td>Mandez</td>
<td>Forward</td>
</tr>
<tr>
<td>2</td>
<td>Rooney</td>
<td>David</td>
<td>Forward</td>
</tr>
<tr>
<td>3</td>
<td>Nani</td>
<td>Mandez</td>
<td>Winger</td>
</tr>
</tbody></table>
<br />
<table class="tbl">
<tbody>
<tr>
<th>Player Id</th>
<th>Club</th>
</tr>
<tr>
<td>1</td>
<td>Juventus</td>
</tr>
<tr>
<td>1</td>
<td>Real Madrid</td>
</tr>
<tr>
<td>1</td>
<td>Manchester United</td>
</tr>
<tr>
<td>1</td>
<td>Sporting Lisbon</td>
</tr>
<tr>
<td>2</td>
<td>DC United</td>
</tr>
<tr>
<td>2</td>
<td>Manchester United</td>
</tr>
<tr>
<td>2</td>
<td>Everton</td>
</tr>
<tr>
<td>3</td>
<td>Manchester United</td>
</tr>
<tr>
<td>3</td>
<td>Sporting Lisbon</td>
</tr>
</tbody></table>
<br />
<hr />
<br />
<strong>3rd Normal Form</strong> <br />
There are data anomalies and inconsistencies in current design, for example: <br />
+ If we deleted the "Ronaldo" and "Nani", we would inevitably delete the agent "Mandez" completely from the database.<br />
+ We cannot add a new agent to the database unless we also add a player.<br />
+ If "Mandez" retired from agent job, we would have to change it in all records in which he appears.<br />
So, we will remove this transitive functional dependency in 3NF. Agents will be moved to another table and its id will be used as foreign key in Player table.
<br />
<table class="tbl">
<tbody>
<tr>
<th>Player Id</th>
<th>Player Name</th>
<th>Agent Id</th>
<th>Position</th>
</tr>
<tr>
<td>1</td>
<td>Ronaldo</td>
<td>1</td>
<td>Forward</td>
</tr>
<tr>
<td>2</td>
<td>Rooney</td>
<td>2</td>
<td>Forward</td>
</tr>
<tr>
<td>3</td>
<td>Nani</td>
<td>1</td>
<td>Winger</td>
</tr>
</tbody></table>
<br />
<table class="tbl">
<tbody>
<tr>
<th>Agent Id</th>
<th>Name</th>
</tr>
<tr>
<td>1</td>
<td>Mandez</td>
</tr>
<tr>
<td>2</td>
<td>David</td>
</tr>
</tbody></table>
Now, we have "Players", "Agents", "Players_Clubs" tables. <br />
We can optimized by moving Clubs into "Clubs" table and linking its id as foreign key in "Players_Clubs" table.
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com7tag:blogger.com,1999:blog-5708060978115865577.post-85226940519794866562019-01-12T20:04:00.001-08:002019-01-12T20:10:31.644-08:00How can we dynamically generate pivot table in MySQL?<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
Pivot table is an important concept we are using in many applications. Technically, it generate the data values into dynamic columns. One of the examples is attendance information. We store the person name, date and present (boolean that determined if that person is present or absent on this date).
<br />
<br /></div>
<div>
<table border="1">
<tbody>
<tr>
<th class="tg-0pky">This is how data is stored is database.</th>
<th class="tg-0pky">This is what we want to see in report.</th>
</tr>
<tr>
<td style="padding: 1px; vertical-align: top;"><table border="1">
<tbody>
<tr>
<th class="tg-0pky">id</th>
<th class="tg-0pky">student</th>
<th class="tg-0pky">date</th>
<th class="tg-0pky">present</th>
</tr>
<tr>
<td class="tg-0pky">1</td>
<td class="tg-0pky">John</td>
<td class="tg-0pky">2019-01-07</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">2</td>
<td class="tg-0pky">David</td>
<td class="tg-0pky">2019-01-07</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">3</td>
<td class="tg-0pky">Larry</td>
<td class="tg-0pky">2019-01-07</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">4</td>
<td class="tg-0pky">Wesley</td>
<td class="tg-0pky">2019-01-07</td>
<td class="tg-0pky">0</td>
</tr>
<tr>
<td class="tg-0pky">5</td>
<td class="tg-0pky">Amy</td>
<td class="tg-0pky">2019-01-07</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">6</td>
<td class="tg-0pky">John</td>
<td class="tg-0pky">2019-01-08</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">7</td>
<td class="tg-0pky">David</td>
<td class="tg-0pky">2019-01-08</td>
<td class="tg-0pky">0</td>
</tr>
<tr>
<td class="tg-0pky">8</td>
<td class="tg-0pky">Larry</td>
<td class="tg-0pky">2019-01-08</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">9</td>
<td class="tg-0pky">Wesley</td>
<td class="tg-0pky">2019-01-08</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">10</td>
<td class="tg-0pky">Amy</td>
<td class="tg-0pky">2019-01-08</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">11</td>
<td class="tg-0pky">John</td>
<td class="tg-0pky">2019-01-09</td>
<td class="tg-0pky">0</td>
</tr>
<tr>
<td class="tg-0pky">12</td>
<td class="tg-0pky">David</td>
<td class="tg-0pky">2019-01-09</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">13</td>
<td class="tg-0pky">Larry</td>
<td class="tg-0pky">2019-01-09</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">14</td>
<td class="tg-0pky">Wesley</td>
<td class="tg-0pky">2019-01-09</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">15</td>
<td class="tg-0pky">Amy</td>
<td class="tg-0pky">2019-01-09</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">16</td>
<td class="tg-0pky">John</td>
<td class="tg-0pky">2019-01-10</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">17</td>
<td class="tg-0pky">David</td>
<td class="tg-0pky">2019-01-10</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">18</td>
<td class="tg-0pky">Larry</td>
<td class="tg-0pky">2019-01-10</td>
<td class="tg-0pky">0</td>
</tr>
<tr>
<td class="tg-0pky">19</td>
<td class="tg-0pky">Wesley</td>
<td class="tg-0pky">2019-01-10</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">20</td>
<td class="tg-0pky">Amy</td>
<td class="tg-0pky">2019-01-10</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">21</td>
<td class="tg-0pky">John</td>
<td class="tg-0pky">2019-01-11</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">22</td>
<td class="tg-0pky">David</td>
<td class="tg-0pky">2019-01-11</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">23</td>
<td class="tg-0pky">Larry</td>
<td class="tg-0pky">2019-01-11</td>
<td class="tg-0pky">0</td>
</tr>
<tr>
<td class="tg-0pky">24</td>
<td class="tg-0pky">Wesley</td>
<td class="tg-0pky">2019-01-11</td>
<td class="tg-0pky">1</td>
</tr>
<tr>
<td class="tg-0pky">25</td>
<td class="tg-0pky">Amy</td>
<td class="tg-0pky">2019-01-11</td>
<td class="tg-0pky">0</td>
</tr>
</tbody></table>
</td>
<td style="padding: 1px; vertical-align: top;"><table border="1">
<tbody>
<tr>
<th class="tg-0lax">date</th>
<th class="tg-0lax">John</th>
<th class="tg-0lax">David</th>
<th class="tg-0lax">Larry</th>
<th class="tg-0lax">Wesley</th>
<th class="tg-0lax">Amy</th>
</tr>
<tr>
<td class="tg-s268">2019-01-07</td>
<td class="tg-s268">1</td>
<td class="tg-s268">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">0</td>
<td class="tg-0lax">1</td>
</tr>
<tr>
<td class="tg-s268">2019-01-08</td>
<td class="tg-s268">1</td>
<td class="tg-s268">0</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
</tr>
<tr>
<td class="tg-s268">2019-01-09</td>
<td class="tg-s268">0</td>
<td class="tg-s268">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
</tr>
<tr>
<td class="tg-0lax">2019-01-10</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">0</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
</tr>
<tr>
<td class="tg-0lax">2019-01-11</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">0</td>
<td class="tg-0lax">1</td>
<td class="tg-0lax">0</td>
</tr>
</tbody></table>
</td>
</tr>
</tbody></table>
<br />
<b>SQL Code</b><br />
<i>SET @dynamic_columns = NULL;</i><br />
<i>select group_concat(student_col) from (</i><br />
<i><span style="white-space: pre;"> </span>select DISTINCT CONCAT('MIN(IF(student = ''', student, ''', present, NULL)) AS ', student) as student_col from </i><br />
<i><span style="white-space: pre;"> </span>attendance</i><br />
<i><span style="white-space: pre;"> </span>) column_list INTO @dynamic_columns;</i><br />
<i>SET @sql = CONCAT('SELECT date, ', @dynamic_columns, ' FROM attendance GROUP BY date');</i><br />
<i>PREPARE qry FROM @sql;</i><br />
<i>EXECUTE qry;</i><br />
<i>DEALLOCATE PREPARE qry;</i></div>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com4tag:blogger.com,1999:blog-5708060978115865577.post-12119719243690729402019-01-12T12:16:00.000-08:002019-01-12T12:16:08.746-08:00Retrieve all the employees under a manager from a self-referencing table in MySQL<div dir="ltr" style="text-align: left;" trbidi="on">
In previous post, we discussed how we can manage to store the hierarchical data in MySQL using self-referencing relationship.<br />
Now, we will see how we can retrieve the data back.<br />
Here, we have manager-employees relationship. One manager has other employees (maybe manager or non-manager) working under him. We will retrieve the whole list of employees under a manager.<br />
<br />
<b>SQL Code</b><br />
<i>SET @id := '2';</i><br />
<i>SELECT id, name, managerid, @id</i><br />
<i>FROM (SELECT * FROM employee ORDER BY managerid, name) E </i><br />
<i>WHERE FIND_IN_SET(managerid, @id) > 0</i><br />
<i>AND @id := CONCAT(@id, ',', id);</i><br />
<br />
Let's assume the <b>managerid </b>to search is "2".<br />
We first need to sort employee table (derived table E) by <b>managerid </b>to make sure that records are in correct order. (This is required as we iterate the record from beginning and add to manager list if conditions matched).<br />
<b>FIND_IN_SET </b>returns the position of a string within a list of strings. (<u>Greater than zero means this employee is under him</u>).<br />
If current record work under this manager(s), <b>CONCAT </b>the id of current record into manager list. (He may have other employees under him).<br />
<br /></div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com4tag:blogger.com,1999:blog-5708060978115865577.post-75852453179712703542019-01-10T17:47:00.000-08:002019-01-12T12:20:47.007-08:00How can we store self-referencing entities in MySQL?<div dir="ltr" style="text-align: left;" trbidi="on">
<b>Self-referencing</b> entities are one of the database designs we are facing in many applications.<br />
An <i>employee </i>will have direct reporting <i>manager </i>who is also an employee.<br />
Each <i>person </i>has <i>mother </i>who also has her own mother and so on.<br />
We can efficiently store those type of hierarchical data in RDBMS.<br />
This type of relationship is called as <i>recursive association</i> which connect to the same class type.<br />
Well, let's have some hands-on work now.<br />
<br />
Following SQL code will create the employee table with self-referencing key.<br />
<i>CREATE TABLE `employee` (</i><br />
<i> `id` int(11) NOT NULL AUTO_INCREMENT,</i><br />
<i> `name` varchar(45) DEFAULT NULL,</i><br />
<i> `managerid` int(11) DEFAULT NULL,</i><br />
<i> PRIMARY KEY (`id`),</i><br />
<i> KEY `index2` (`managerid`),</i><br />
<i> CONSTRAINT `fk` FOREIGN KEY (`managerid`) REFERENCES `employee` (`id`) ON DELETE SET NULL ON UPDATE CASCADE</i><br />
<i>) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;</i><br />
<i><br /></i>
After creating the table, we will enter the dummy data rows to test.<br />
<i>INSERT INTO employee(name) values('CEO');</i><br />
<i>INSERT INTO employee(name, managerid) values('Mgr1', 1);</i><br />
<i>INSERT INTO employee(name, managerid) values('Mgr2', 1);</i><br />
<i>INSERT INTO employee(name, managerid) values('TL1', 2);</i><br />
<i>INSERT INTO employee(name, managerid) values('TL2', 3);</i><br />
<i><br /></i>
Now, we can easily retrieve the values from our self-referencing table using following SQL join query.<br />
<i>SELECT EMP.id, EMP.name AS "Employee", MGR.name AS "Manager"</i><br />
<i>FROM employee EMP LEFT OUTER JOIN employee MGR</i><br />
<i>ON EMP.managerID = MGR.id</i><br />
<i>ORDER BY EMP.managerid, EMP.name</i><br />
<i><br /></i>
Output<br />
<b>Id<span style="white-space: pre;"> </span>Employee<span style="white-space: pre;"> </span>Manager</b><br />
1<span style="white-space: pre;"> </span>CEO<span style="white-space: pre;"> </span><span style="white-space: pre;"> </span>(null)<br />
2<span style="white-space: pre;"> </span>Mgr1<span style="white-space: pre;"> </span><span style="white-space: pre;"> </span>CEO<br />
3<span style="white-space: pre;"> </span>Mgr2<span style="white-space: pre;"> </span><span style="white-space: pre;"> </span>CEO<br />
4<span style="white-space: pre;"> </span>TL1<span style="white-space: pre;"> </span><span style="white-space: pre;"> </span>Mgr1<br />
5<span style="white-space: pre;"> </span>TL2<span style="white-space: pre;"> </span><span style="white-space: pre;"> </span>Mgr2</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com4tag:blogger.com,1999:blog-5708060978115865577.post-35849845407543197022018-12-24T20:09:00.003-08:002018-12-24T20:09:53.894-08:00Access Java REST API from Angular application...<div dir="ltr" style="text-align: left;" trbidi="on">
This page is under construction.</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com1tag:blogger.com,1999:blog-5708060978115865577.post-46955052553842171352018-12-24T20:08:00.000-08:002019-01-12T13:00:47.207-08:00How to consume REST API secured by Spring Security?<div dir="ltr" style="text-align: left;" trbidi="on">
In this article, I am going to demostrate how we could access the REST api secured by Spring Security to display in front-end. We are going to use <a href="https://spring.io/tools">Spring Tool Suite (STS)</a>.<br />
Create a new Java Project in STS and add the files into it similar to the following structure.<br />
│ pom.xml<br />
├───src<br />
│ └───main<br />
│ ├───java<br />
│ │ └───com<br />
│ │ └───p1coder<br />
│ │ │ MvcConfiguration.java<br />
│ │ │ SecurityConfig.java<br />
│ │ │ SpringBootWebApplication.java<br />
│ │ ├───controller<br />
│ │ │ ProductController.java<br />
│ │ │ RequestHandler.java<br />
│ │ ├───domain<br />
│ │ │ Product.java<br />
│ │ │ UserPreference.java<br />
│ │ └───service<br />
│ │ ProductService.java<br />
│ │ RestApiService.java<br />
│ └───webapp<br />
│ └───WEB-INF<br />
│ └───views<br />
│ └───product<br />
│ display.jsp<br />
Fill each file with the sample code from each code section.
<br />
<br />
<details>
<summary>MvcConfiguration.java</summary>
<script src="https://gist.github.com/p1coderblog/f38ed09b1806f838e4fbf0abd2dd7e05.js"></script>
</details>
<details>
<summary>SecurityConfig.java</summary>
<script src="https://gist.github.com/p1coderblog/51b6b6371c8e5a31dee6fc4bfde59f91.js"></script>
</details>
<details>
<summary>SpringBootWebApplication.java</summary>
<script src="https://gist.github.com/p1coderblog/5afce7f023ad0f305920a70de377658c.js"></script>
</details>
<details>
<summary>ProductController.java</summary>
<script src="https://gist.github.com/p1coderblog/71dfba9a2a256447c2b900f7d8502e22.js"></script>
</details>
<details>
<summary>RequestHandler.java</summary>
<script src="https://gist.github.com/p1coderblog/d158972fecfc8bd2fe4e7700495d2079.js"></script>
</details>
<details>
<summary>ProductController.java</summary>
<script src="https://gist.github.com/p1coderblog/71dfba9a2a256447c2b900f7d8502e22.js"></script>
</details>
<details>
<summary>RequestHandler.java</summary>
<script src="https://gist.github.com/p1coderblog/d158972fecfc8bd2fe4e7700495d2079.js"></script>
</details>
<details>
<summary>Product.java</summary>
<script src="https://gist.github.com/p1coderblog/6fec1ad6b5969908399f362def4734ef.js"></script>
</details>
<details>
<summary>UserPreference.java</summary>
<script src="https://gist.github.com/p1coderblog/0f864288ac180bded2ad1da3eaa830d1.js"></script>
</details>
<details>
<summary>ProductService.java</summary>
<script src="https://gist.github.com/p1coderblog/a1d6215a95ff50d63530c64e581ed111.js"></script>
</details>
<details>
<summary>RestApiService.java</summary>
<script src="https://gist.github.com/p1coderblog/d555ac364b5484d23d95d16a35a4162c.js"></script>
</details>
<details>
<summary>display.jsp</summary>
<script src="https://gist.github.com/p1coderblog/42fa212a1a65635c121770a315bb246f.js"></script>
</details>
<details>
<summary>pom.xml</summary>
<script src="https://gist.github.com/p1coderblog/3aaaa94da3a310fb305facc070884b85.js"></script>
</details>
After maven update the reference packages, we can execute the sample by right click on project and choosing "Run as" -> "Spring Boot App".<br />
In RequestHandler.java, we have REST api routes accessible by either <b>PathVariable</b> or <b>RequestParam</b>, which read the repository layer data.<br />
If we directly access "http://localhost:8080/getAllProducts" api, we will get authentication dialog box as we secure the route ("/getAllProducts**") in "SecurityConfig.java" file. This route is granted only to "admin" role by extending the <b>WebSecurityConfigurerAdapter</b> class.<br />
We will see the sample listing when we access "http://localhost:8080/products" page. We implement the data retrieval logic and pass api credential to REST api.<br />
In RestApiService.java, <b>BasicAuthenticationInterceptor</b> is used to authenticate while retrieving by HTTP GET execute on <i>RestTemplate</i> instance.
<br />
In ProductController.java, <b>ObjectMapper</b> instance is used to deserialize the generic return value from data service call instantiated by <i>@Autowired</i> <b>Dependency Injection</b>.
<br />
<details>
<summary>Output</summary>
<img src="data:image/JPG;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4RDgRXhpZgAATU0AKgAAAAgABAE7AAIAAAAHAAAISodpAAQAAAABAAAIUpydAAEAAAAOAAAQyuocAAcAAAgMAAAAPgAAAAAc6gAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBoaWxpcAAAAAWQAwACAAAAFAAAEKCQBAACAAAAFAAAELSSkQACAAAAAzYxAACSkgACAAAAAzYxAADqHAAHAAAIDAAACJQAAAAAHOoAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyMDE4OjEyOjMwIDEyOjA0OjA2ADIwMTg6MTI6MzAgMTI6MDQ6MDYAAABQAGgAaQBsAGkAcAAAAP/hCxlodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkJz8+DQo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIj48cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPjxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSJ1dWlkOmZhZjViZGQ1LWJhM2QtMTFkYS1hZDMxLWQzM2Q3NTE4MmYxYiIgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIi8+PHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9InV1aWQ6ZmFmNWJkZDUtYmEzZC0xMWRhLWFkMzEtZDMzZDc1MTgyZjFiIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPjx4bXA6Q3JlYXRlRGF0ZT4yMDE4LTEyLTMwVDEyOjA0OjA2LjYxMjwveG1wOkNyZWF0ZURhdGU+PC9yZGY6RGVzY3JpcHRpb24+PHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9InV1aWQ6ZmFmNWJkZDUtYmEzZC0xMWRhLWFkMzEtZDMzZDc1MTgyZjFiIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPjxkYzpjcmVhdG9yPjxyZGY6U2VxIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+PHJkZjpsaT5QaGlsaXA8L3JkZjpsaT48L3JkZjpTZXE+DQoJCQk8L2RjOmNyZWF0b3I+PC9yZGY6RGVzY3JpcHRpb24+PC9yZGY6UkRGPjwveDp4bXBtZXRhPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSd3Jz8+/9sAQwAHBQUGBQQHBgUG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KKKACiuVufGNxBczTppaPpFtfLYT3RusTCQuqbli2EFA7gElweCQpwM9VR0uHWwUUUUAFFFFABRRUV1P9ms5p9u7yo2fbnGcDOKTaSuxpNuyJaKxJPEfl+BR4j+y5zYrefZ/M9UDbd2PfGcfhVTW9f17S9Wsra10bTrmG/uPs9vLJqckbbvLZyXUQMFHyMOCe34U007MlNNXR01Fc++u6pJc/wBn6fpdtcanDGsl4GvWS2t92cL5vllmY4yAI+nJIyu7W02a+nslfVLSKzudzBooZ/OTAJAIfapIIweVB5pDLVFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQB5lqNlK9xes+m6mfEraj5tsIreU2LhXHkyOQPIOIwuWf94CDgghMem/WiijpYOtwooooAKKKKACq2oqz6XdoilmaFwFAySdp4qzRSkrpoadnc84l8Fxr8Kl2LrZ1AaUn+inU7tv3nlj5PJMm3rxs247YrpvEVtPPq/hl4YZJFg1IvKyISI1+zyjLegyQMnuRXQ0Vbld3IjHlVvKxyq3EnhjxJq019ZXk9jqkqXMNxZWklyUcRJG0bpGrOPuBg2NvJGQcZ6HT71dRs1uY4biFHJ2rcwtE5AOMlGwy59CAfarNFT0KCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/9k=" />
</details>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com20tag:blogger.com,1999:blog-5708060978115865577.post-56387486025910702018-11-11T14:29:00.002-08:002019-01-12T13:03:21.026-08:00How to create CRUD feature using AngularJS?<div dir="ltr" style="text-align: left;" trbidi="on">
We need the basic features for LOB applications: Adding new Records, Reading all existing records, Updating the desired record and Removing the selected record. I put together all this into one piece using AngularJS.
<br />
In real-world app, we use WebAPI for backend service. Here, just only client side code is used for demonstration.
<br />
The beauty of MVC framework is showcased.
<br />
Bootstrap is used for styling.
<br />
<br />
<details>
<summary>Source Code</summary>
<script src="https://gist.github.com/p1coderblog/7e2049a6f113b313093154c907634da6.js"></script>
</details>
<br />
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"></link>
<script>
angular.module('MainApp', []).controller('MainCtrl', function ($scope) {
$scope.editIndex = '';
$scope.userList = [
{ Name: 'John', Age: '20' },
{ Name: 'David', Age: '23' },
{ Name: 'Terry', Age: '25' },
{ Name: 'Lucas', Age: '24' },
{ Name: 'Adam', Age: '19' },
{ Name: 'Peter', Age: '20' }
];
$scope.edit = function (id) {
$scope.editIndex = id;
$scope.currentObject = angular.copy($scope.userList[id]);
}
$scope.save = function () {
if (!$scope.userList[$scope.editIndex]) {
$scope.userList.push($scope.currentObject);
} else {
$scope.userList[$scope.editIndex] = $scope.currentObject;
}
$scope.currentObject = {};
$scope.editIndex = '';
}
$scope.delete = function (id) {
$scope.userList.splice(id, 1);
}
});
</script>
</head>
<body>
<div ng-app="MainApp" ng-controller="MainCtrl">
<div>
<form class="form-horizontal">
<div class="form-group col-sm-4">
<label class="control-label" for="name">Name</label>
<input class="form-control" id="name" ng-model="currentObject.Name" placeholder="Name" type="text" />
<label class="control-label" for="age">Age</label>
<input class="form-control" id="age" ng-model="currentObject.Age" placeholder="Age" type="text" />
<input class="ng-valid" ng-model="$scope.editIndex" type="hidden" />
<br />
<button class="btn btn-primary btn" ng-click="save()" type="button"><i class="glyphicon glyphicon glyphicon-ok-circle"></i>Save</button>
</div>
</form>
</div>
<table class="table table-striped table-bordered">
<thead>
<tr><th>Sr</th><th>Name</th><th>Age</th><th>Action</th></tr>
</thead>
<tbody>
<tr ng-repeat="user in userList track by $index">
<td>{{$index + 1}}</td>
<td>
{{user.Name}}
</td>
<td>{{user.Age}}</td>
<td>
<div class="btn-group">
<button class="btn btn-default btn" ng-click="edit($index);" type="button"><i class="glyphicon glyphicon-pencil"></i></button>
<button class="btn btn-default btn" ng-click="delete($index);" type="button"><i class="glyphicon glyphicon-trash" title="{{user.id}}"></i></button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com4tag:blogger.com,1999:blog-5708060978115865577.post-88279661779217485702018-10-30T19:51:00.000-07:002018-12-24T13:54:45.038-08:00How to create a SVG PieChart using AngularJS?<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
A couple of years back, I worked in a project which require data visualization component. There are a set of performance and features requirements set by the end-users. I gained so much experience and knowledge from that project. Now, I got something which is kind of similar to what I have done and did the basic charting work. Well, small program which do something cool. A SVG chart drawn by pure JavaScript...also it's Angular Component.<br />
<br />
<details>
<summary>Source Code</summary>
<script src="https://gist.github.com/p1coderblog/8330227fd0853526444f6a9c832c8bc9.js"></script>
</details>
<br />
<i>It is how it works...</i></div>
</div>
</div>
<br />
<head>
<meta charset="UTF-8">
<title>AngularJS sample</title>
<style>
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.slider {
width: 200px;
}
.pie {
width: 250px;
height: 250px;
}
.pie circle {
fill: lightgray;
}
.pie path {
fill: none;
stroke: #436689;
}
</style>
<script src="https://code.angularjs.org/1.7.5/angular.min.js"></script>
<script>
angular.module('components', [])
.directive('slider', function () {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function ($scope, $element) {
$scope.$watch('value', function (val) {
setTimeout(function () {
updatePie($scope.max, val);
}, 0);
});
$scope.value = 5;
$scope.max = 20;
$scope.min = 0;
$scope.setValue = function () {
$scope.value = 17;
};
setTimeout(function () {
initPie();
}, 0);
function convertToCartesian(center, radius, degree) {
var radian = (degree - 90) * Math.PI / 180.0;
return {
x: center + (radius * Math.cos(radian)),
y: center + (radius * Math.sin(radian))
};
}
function updatePie(total, value) {
let div = document.getElementById($scope.$id);
if (!div) {
return;
}
let svg = div.getElementsByClassName('pie')[0];
let radius = svg.getBoundingClientRect().width / 2;
let path = svg.getElementsByClassName('completed')[0];
let percentage = (360 * value) / total;
path.setAttribute('d', calculateArc(radius, percentage));
}
function initPie() {
let div = document.getElementById($scope.$id);
if (!div) {
return;
}
let svg = div.getElementsByClassName('pie')[0];
let radius = svg.getBoundingClientRect().width / 2;
let path = svg.getElementsByClassName('completed')[0];
let circle = svg.getElementsByClassName('bg')[0];
path.setAttribute('stroke-width', radius);
circle.setAttribute('cx', radius);
circle.setAttribute('cy', radius);
circle.setAttribute('r', radius);
}
function calculateArc(center, endAngle) {
let startAngle = 0;
let radius = center / 2;
let start = convertToCartesian(center, radius, endAngle - 0.0001);
let end = convertToCartesian(center, radius, startAngle);
let flag = endAngle - startAngle <= 180 ? "0" : "1";
return [
"M", start.x, start.y,
"A", radius, radius, 0, flag, 0, end.x, end.y
].join(" ");
}
},
template: '<div id={{$id}} class="container">' +
'<button ng-click="setValue()">setValue</button>' +
'<br /><input type="range" max="{{max}}" min="{{min}}" ng-model="value" class="slider" />' +
'<br />Current value: {{value}}<br /><br />' +
'<svg class="pie">' +
'<circle class="bg" />' +
'<path class="completed"></path>' +
'</svg></div>',
replace: true
};
});
angular.module('myApp', ['components']);
</script>
</head>
<body ng-app="myApp">
<slider></slider>
</body>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com3tag:blogger.com,1999:blog-5708060978115865577.post-91356760515677164852018-10-14T06:24:00.000-07:002018-11-11T14:06:37.034-08:00How can we create a bouncing ball without using any JavaScript?<div dir="ltr" style="text-align: left;" trbidi="on">
Animating the UI is an effective way to draw the attention from users and make our apps more interesting. When I was working on a Silverlight to HTML5 project, I come across many animated user interface. Our roadmap is not to use jQuery dependent code unless we have to. From this project, I learnt many things on <i>CSS animation</i> and it is highly efficient and effective way of doing animation.<br />
We do not clutter the JavaScript code.<br />
We reduce the execution overhead and save CPU cycle.<br />
Our code will stay current and relevant as new technologies come up.<br />
We have no dependency on any other third party library as we use pure native features from what browsers support.<br />
***No workaround or hacky stuff.<br />
Here I want to share a sample code which is of a little something similar to how I did UI animation.<br />
I make it bare minimum to make it more intuitive and easy to understand.<br />
Just copy the sample html, save it and open it in a <i>Chrome, Firefox, IE11 or Edge</i> browsers.<br />
You will see the ball bouncing inside its container - without any JavaScript required.<br />
<br />
<br />
<details>
<summary>Source code</summary>
<script src="https://gist.github.com/p1coderblog/e01311fcca38e9fa0615e967786ca5dd.js"></script>
</details>
<br />
<b>Code in Action</b><br />
<div>
<style type="text/css">
.playground {
height: 500px;
width: 500px;
margin: 0 auto;
position:relative;
border: 1px solid #000;
}
.ball {
height: 20px;
width: 20px;
position: absolute;
background-color: #1B34EB;
border-radius: 10px;
animation: vert 4s infinite linear;
}
.bar {
width: 20px;
position: absolute;
animation: horz 5s infinite linear;
}
@keyframes vert {
0%, 100% { top: 0; }
50% { top: 480px; }
}
@keyframes horz {
0%, 100% { left: 0; }
50% { left: 480px; }
}
</style>
<br />
<div class="playground">
<div class="bar">
<div class="ball">
</div>
</div>
</div>
</div>
</div>
P1Coderhttp://www.blogger.com/profile/03305438256988995394noreply@blogger.com2