tag:blogger.com,1999:blog-87179904828171800892024-03-19T12:35:48.260-07:00A Piece of My CodeGabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.comBlogger62125tag:blogger.com,1999:blog-8717990482817180089.post-67876609220688547742021-06-08T06:58:00.001-07:002021-06-08T06:58:12.885-07:00My tips for recruiters (part 1)<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipsb9OEgj7rPmWGEIVmQ4H9XJilfSZIP7M4UEzr9BnM_YFXdFd7RAHA_aGnHZP5-z7kndA7qJUL4sWyjsLN6TjprguWlYx0iZizQQnZmZERUl2RfW6AEOHLGJYzmkBXnrXhZYHsj0CXXM/s720/q9n4vis3xy561.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="351" data-original-width="720" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipsb9OEgj7rPmWGEIVmQ4H9XJilfSZIP7M4UEzr9BnM_YFXdFd7RAHA_aGnHZP5-z7kndA7qJUL4sWyjsLN6TjprguWlYx0iZizQQnZmZERUl2RfW6AEOHLGJYzmkBXnrXhZYHsj0CXXM/s320/q9n4vis3xy561.jpeg" width="320" /></a></div><br /><p><br /></p><p>It is no secret that IT people is on high demand of highly talented experienced professionals. It has been like that for several years as far as I remember. But with the introduction of LinkedIn as a place for exposing your professional profile to the world, we, computer geeks now feel as an elite group that can have the privilege of rejecting multiple job offers on daily basis. Sometimes (and I'm not saying it in a pretentious manner) you feel like you're writing back letters from fans. "Dear recruiter, thanks for your offering, but at this moment....".</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQH3LcAkQCttn0iyE5FWOV5vw2ZU_6QHehZKRGKGmZ0cm1OQRPZ0v8tzeTlkXa-JatjabrxEhUke7zzImvfwL7vGMc4cTq1RI5zUIOIO0etxG1SRcWSJ0op_uQoPYfloPtUQmVBupb2zY/s1024/RingoStarr_Simpsons-1570810066-1024x604.jpeg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="604" data-original-width="1024" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQH3LcAkQCttn0iyE5FWOV5vw2ZU_6QHehZKRGKGmZ0cm1OQRPZ0v8tzeTlkXa-JatjabrxEhUke7zzImvfwL7vGMc4cTq1RI5zUIOIO0etxG1SRcWSJ0op_uQoPYfloPtUQmVBupb2zY/s320/RingoStarr_Simpsons-1570810066-1024x604.jpeg" width="320" /></a></div><br /><p><br /></p><p>This amount of job offers is one the explanations of why so many of us don't stay in a single company more than 5 years. There is so much competition out there, that the moment you start getting a little bored with your current project, you can just start paying more attention to the market place and let the recruiters tell you nice things about other places. But even though there is plenty jobs out there, it doesn't mean we feel safe accepting any offer. Not even listening to many of them. In the last years I have received a very big number of invitations from recruiters, that now my contacts in LinkedIn surpasses the amount of contacts I have combined in my other social networks. And of course I didn't swap jobs hundreds of times.</p><p>After receiving all these messages from recruiters, in which 90% of the ended in a template response of "Thanks, but no thanks" I decided that maybe I could give some advices from my experience to IT recruiters of the world on how to fish a little better. Or at least get the "fishes" more interested in the "bait".</p><p>I went to the list of messages I have on my inbox and came with these list of recommendations for IT recruiters. I know maybe there would be refutals from what I'm going to recommend. I will be glad to listen to different opinions. I have no recruitment experience, so from my perspective certain things are not efficient, but maybe they do get the job done in the ancient art of convincing people to move to other places. These are my first 5 tips for recruiters (this could be a multi-part post).</p><p><br /></p><h3 style="text-align: left;">Get familiar with the technologies</h3><p>I know HR people don't need to be an expert on the field. But I can assure you would be more effective if you have a grasp of certain technologies, so you do contact people with the right experience for the position. For example, I am a developer with a lot of experience in Java programming language. If you're looking for a Tech Lead for a .NET team, unless you do me a super offer that makes me betray my for years language of preference, I will skip immediately the offer. I'm a nice guy and always respond saying that the position doesn't fit my profile. But is everyone as nice as me?</p><p><br /></p><h3 style="text-align: left;">Personalize your message</h3><p>Recently, I received a job offers, that I was almost entirely convinced that I was not going to accept, but it was personalized in a way that I never saw before, that made me at least have an initial call from the recruiter. The message read something like this: "It looks like you are rocking in Ducky Inc. with your new position, but do you want to move your career further". The message showed that the recruiter took at least some time to read my profile and understand my experience. That simple detail is not common. Or maybe recruiters do read in detail our profiles, but it's not reflected in the initial message. Normally it's a repeated phrase like "your profile caught our attention" or "you look like a good fit for our position". Is it too much work to try to find a little detail from the candidate's profile and use it in the message? It could be that indeed is faster and simpler to send a templated message.</p><p><br /></p><h3 style="text-align: left;">Give details about the employer</h3><p>"A very important international company is looking for [INSERT_POSITION_HERE]". Many professionals are willing to consider an offer based on the potential employer. Someone might want to change from financial related work, to develop fitness applications to mention an example. Not everything is about money or promotions. The thing is that many messages from recruiters look like a secret message from a spy agency (this message will self-destroy after 5 seconds). I can guess that perhaps some recruitment companies don't like to mention their client because they feel that the candidate could contact it directly. I don't know. But not giving details about the future employer is a big deal break for me. </p><p><br /></p><h3 style="text-align: left;">Send links of the work environment</h3><p>This is a very important aspect to consider. It may change a lot in this new normality where companies are subscribing more and more to remote work, but still a lot of them want their employees to see face to face with certain regularity. Showing videos and pictures of the work environment (this may sound weird) makes the offer a little more human/warmth. If you only have a brief description of the company with the benefits offered, you need to imagine how the job place looks or go and research on your own. Better to ease the job by showing candidates how cool could look their new second home.</p><p><br /></p><h3 style="text-align: left;">Does it hurt to put salary ranges?</h3><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgO60YNnQkkA6lLQl-UQMI4911qKqoUNc-jHYiKO8p6wYwfpF1sny7WUS6ZwMGc0bnJtRtGXcc3uEedkmLt_4CQza6xlNVc1R5yvCeWcmVv_eHHqc5GZqX08I-iOK0EXuAFgCd1xHkEcIU/s1524/Screen+Shot+2021-06-07+at+9.09.14+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="890" data-original-width="1524" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgO60YNnQkkA6lLQl-UQMI4911qKqoUNc-jHYiKO8p6wYwfpF1sny7WUS6ZwMGc0bnJtRtGXcc3uEedkmLt_4CQza6xlNVc1R5yvCeWcmVv_eHHqc5GZqX08I-iOK0EXuAFgCd1xHkEcIU/s320/Screen+Shot+2021-06-07+at+9.09.14+PM.png" width="320" /></a></div><br /><p>A job offer could sound very nice and interesting, but unfortunately, a lot of us have responsibilities and families to care for. That means that changing jobs with significant less salary is not very wise. That would make salary expectations almost 90-100% of the cases the first filter we put at from in order to decide if we proceed to start a process, or at least have the first call with the recruiter. So why not saving time for both and put salary ranges in the offer?</p><p>Now with all these years of experience I have learned to feel less embarrassed by asking high if no salary range is provided. It's a gamble! But if you are not really in any rush to change a job, then you're not really risking much. On the contrary, if a salary range is provided, I can ponder other aspects of the position. Maybe there is not big increase in salary compensation, but there is a strategic move in the career by the experience that would be gain. </p>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-7976357836437652222020-12-09T19:21:00.001-08:002020-12-09T19:21:59.938-08:00WFH 2.0<p>As part of the human specie living the events of the 2020 year, you are probably very familiar with the top one cliche phrase of the year: "the new normal". This phrase is used to describe what apparently has become our continuous present, implying that there was an "old normal", and we need to accept the new reality. The phrase has a resignation emotional charge, a certain early nostalgia for what in appearance had been lost forever. Nothing is going to be the same... This of course it totally debatable, but I think most of us already consider that sooner or later we will start enjoying those precious things that this virus put on hold. In any case, I do think there are new normal things that came to our lives to stay, probably forever. One of them is what I call the WFH 2.0 (Working From Home 2.0).</p><p> WFH is nothing revolutionary. If you are reading this, there is high chance that you were already enjoying certain level of this benefit that your workplace grants you. Maybe one day, maybe a couple, some were only required to go one day to the office, and some others were 100% from home. We know not all companies can afford this benefit, but for those that can, you could say that the amount of days given to work from home is somehow correlated with the level of trust the company has of its employees. The higher the flexibility to WFH, the higher the maturity a company has in its processes to keep things aligned with the vision, culture, quality, client satisfaction, etc.</p><p>Then COVID-19 came and suddenly many companies started to realized that perhaps office space is not a guarantee for work performance. During this pandemic almost every IT company was forced to a POC (Proof Of Concept) to let all employees work remotely for months. And surprise, surprise, nothing really has collapsed (AFAIK). This realization is starting to have great consequences (depending of your point of view). Many high profile companies like Microsoft has already <a href="https://www.usatoday.com/story/money/2020/10/11/microsoft-reveals-permanent-work-home-policy/5960462002/" target="_blank">announced measures to let employees work from home permanently</a> And this is not a luxury only big companies can do. All size companies are considering the same. And why not? There is a win-win situation. Employees appreciate not rushing into traffic, spending more time with family, better balance with work and personal life. You name it. Companies in the other hand saves in office space, amenities, water, electricity and whatever other spend that was included to attract high talent.</p><p>But if fully remote is going to become the new normal, how can companies spend the money and efforts to create a better "virtual space" to work? I want to give five of my ideas in case it helps a few HR people. Feel free to comment if I'm going too nuts (I'm just a Software Developer with no studies in HR so take it with a grain of salt). </p><p><b>Entrance bonus for home office improvements</b>. Most of us will start requiring continuous improvements in our work space to make it a better spot. A place where you feel more productive and motivated to give your best. Why not a reimbursement to spend it in a better chair, desktop, extra monitors, keyboard, mouse, even decorations. Sky is the limit...</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoE6nd35X9cYtRxv2R4PwwbCil726BP2AQ4Db7TFskHsQlSARZWCCXepVU5dP_bbjl0EeQv0m6yZtorv3j-CRiuvxhpRUGpsAKN57qYW48pgY4v-5_VFPpLdNO_BoHOXTBVnzfNd3O-ho/s859/mysetup.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="859" data-original-width="750" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoE6nd35X9cYtRxv2R4PwwbCil726BP2AQ4Db7TFskHsQlSARZWCCXepVU5dP_bbjl0EeQv0m6yZtorv3j-CRiuvxhpRUGpsAKN57qYW48pgY4v-5_VFPpLdNO_BoHOXTBVnzfNd3O-ho/s320/mysetup.jpg" /></a></div><br /><p><br /></p><p><b>Team activities that YOU DON'T WANT TO MISS</b>. Yeah, every company organizes team activities like the classics bowling, Go Karts, mini golf, etc. Before these were kind of optional, and let's be honest, who hasn't assisted to one of these just for the sake of not being the "party pooper", or even making excuses because you felt it was not worth to sacrifice family time or other activity for something you really won't enjoy. Having mandatory office days still provided the human in person contact you still need on some basis to keep the team spirit flowing. But now without them, these team activities become more important. Thus, let's make them cooler so people really want to participate. </p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYumWYOp1V__jx_YhpbDKVYoFL0omEyDXxnSj0ZXl5I7BT3YoViQpJ_Vt7BE1_b3D-pXWLZiBMD8jFlqPJrXPjPgpZxPWIZnmz75U9J45krW9U63vhRZBaQKpFaxI_0B7w33R330dcyZE/s480/jurassic-park-spared-no-expense.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="270" data-original-width="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYumWYOp1V__jx_YhpbDKVYoFL0omEyDXxnSj0ZXl5I7BT3YoViQpJ_Vt7BE1_b3D-pXWLZiBMD8jFlqPJrXPjPgpZxPWIZnmz75U9J45krW9U63vhRZBaQKpFaxI_0B7w33R330dcyZE/s320/jurassic-park-spared-no-expense.gif" width="320" /></a></div><br /><p><b>More Interactive Corporate Meetings</b>. On the same line of team activities, if the company has recurrent corporate meeting every, like All Hands (or however is called in your workplace), let's please spend less time on executive reports, and leave more room for interacting/partying. A lot of employees really don't have a lot of time due to other responsibilities (mostly family), which often translates in not staying in the post executive reports parties. Yeah, yeah, we know it's important to know the status of the company, but I believe we can make them a little more executive summary.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwkb1FC4F6yjoPi7vUh_GT8RmTYWywFN-d4swV_xDqcSQNrDNfODnCzjDfxZs2UgZOJGgbsPQ3Ypy8QjYTR33crgnHS0STw8Vux2cPM-QwPLMIvxUH290CZ3dMczwdoM2byCqExFVelMQ/s435/nomoremeetingd.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="250" data-original-width="435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwkb1FC4F6yjoPi7vUh_GT8RmTYWywFN-d4swV_xDqcSQNrDNfODnCzjDfxZs2UgZOJGgbsPQ3Ypy8QjYTR33crgnHS0STw8Vux2cPM-QwPLMIvxUH290CZ3dMczwdoM2byCqExFVelMQ/s320/nomoremeetingd.gif" width="320" /></a></div><br /><p><b>Perks to release the stress</b>. Free lunch was an ideal perk for anyone going to an office. It released you from the burden of preparing your lunch and warm it on the microwave. Now full remote opens a range of perks that employees can enjoy thinking in releasing the stress of being a little more isolated, and taking advantage of the additional time you get by not commuting, looking for appropriate attire, *cough* <span style="font-size: x-small;">not showering</span> *cough*. So how about paying gym, salsa classes, swimming lessons, anything that requires people to leave the house and put the body in motion.</p><p><br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinZajalAPp5Nx7A3bwSWRLaixNOFHhbilFvVVsVnvJ7mOnfK6f676hxD32M4vhdu8gySk227VCtErW2KNUuNhAWPbMadk4eigHL2sYY0CSc4RFZToOelXsS4O74RqZ1-a0oFqR14Gbrr8/s498/spin-class.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="277" data-original-width="498" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinZajalAPp5Nx7A3bwSWRLaixNOFHhbilFvVVsVnvJ7mOnfK6f676hxD32M4vhdu8gySk227VCtErW2KNUuNhAWPbMadk4eigHL2sYY0CSc4RFZToOelXsS4O74RqZ1-a0oFqR14Gbrr8/s320/spin-class.gif" width="320" /></a></div><br /> <p></p><p><b>Go crazy with the goodies!</b> And with crazy I mean smart, creative and fun. Perhaps your company's t-shirt is a little old and needs a renew. A new edition of stickers to put on your laptop. Caps with a cool logo. Anything that is not a waste enters in this category. This may seem like a silly thing, but without being present physically in office space, little reminders of company's identity can help sustain the culture and pride to work in a place that became virtual.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrLFnTDpQnsD7Z6F5B4JJxgRqNzUPLEJW_DuqdeFUJd9QSbtiD63yWFgTc-sL2xXyEsKPeWCwksIXgMcMIaR8BSfkQ6dn0-x-vx-_70nvS4fCPvt_YQlHHE8Ix2gmNksAe6Q0ABWiv1FM/s500/goodies.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="223" data-original-width="500" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrLFnTDpQnsD7Z6F5B4JJxgRqNzUPLEJW_DuqdeFUJd9QSbtiD63yWFgTc-sL2xXyEsKPeWCwksIXgMcMIaR8BSfkQ6dn0-x-vx-_70nvS4fCPvt_YQlHHE8Ix2gmNksAe6Q0ABWiv1FM/s320/goodies.gif" width="320" /></a></div><br /><p><br /></p><p>What else would you suggest????</p>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-43136856175205215522020-11-12T20:21:00.004-08:002020-11-12T20:21:27.876-08:00Java: Solving Anagrams with streams<p>I have been playing with code problems. Since I'm studying for Java 11 certification, I decided to resolve the Anagrams problem using streams. Here what I do is sort the two strings characters and then making a comparison.</p><p><br /></p>
<pre class="java" name="code">public class Anagrams {
static boolean isAnagram(String a, String b) {
return sortStringChars(a).equals(sortStringChars(b));
}
static String sortStringChars(String str) {
return str.toLowerCase().chars()
.mapToObj(c -> (char) c)
.sorted()
.reduce("", (s,c) -> s.concat(String.valueOf(c)), String::concat);
}
public static void main(String[] args) {
String a = "anagram";
String b = "margana";
boolean ret = isAnagram(a, b);
System.out.println( (ret) ? "Yes, Anagrams!" : "Not Anagrams" );
}
}
</pre>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-25377349101213038122020-10-22T22:43:00.006-07:002020-10-22T22:46:45.039-07:00Gradle: Exclude SpringBoot Application Class from Jacoco Coverage<p> </p><p>Jacoco plugin is a must if you care about not just writing random unit tests, but to ensure you have the right coverage for your code base. The plugin also comes with a report that shows the coverage at package level.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ-Uq248apKl1KPC4IR_LaKN69L5Wyt81vsXKnkTz_9uoi378D9S6XWmYaJSfxlMJBiKt8O_UsxMayZlvMUg09-WreZJ_mJJpUQtwh-6VQD3zLP_vsrJlrbdwWeHpbpOvScKUMkzhyphenhyphenZyY/s2260/html-report-jacoco-application.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="2260" height="88" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ-Uq248apKl1KPC4IR_LaKN69L5Wyt81vsXKnkTz_9uoi378D9S6XWmYaJSfxlMJBiKt8O_UsxMayZlvMUg09-WreZJ_mJJpUQtwh-6VQD3zLP_vsrJlrbdwWeHpbpOvScKUMkzhyphenhyphenZyY/w640-h88/html-report-jacoco-application.png" width="640" /></a></div>One thing that has been bothering me with SpringBoot apps I work with is the low number that always appears for the main SpringBoot Application class. This is a boiler plate class with a simple main method to launch the SpringApplication:<p></p><pre class="java" name="code">
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}</pre><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Almost in every application we won't need to add any other additional logic to this method which makes it kind of dumb to create a test just to cover this class and main method. Best thing is to exclude it from our coverage using this format (in this case for Gradle):</div><div class="separator" style="clear: both; text-align: left;"><br /></div>
<pre class="java" name="code">jacocoTestReport {
afterEvaluate {
classDirectories.from = files(classDirectories.files.collect {
fileTree(dir: it, exclude: 'com/mycom/shoppingcart/ServiceShoppingCartApplication.class')
})
}
}
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-8022515585833102982020-04-02T14:03:00.001-07:002020-04-02T14:03:18.976-07:00Spring REST Controller: Resolve conflicts between root controllers endpoints and swagger-ui.html<br />
Our team had some issues setting up swagger. We've been adding swagger to multiple services and for a strange reason, one service was not loading the swagger-ui.html. After some testing, we realize there was a conflict with some endpoint that were mapping to the root of the site:<br />
<br />
<pre class="java" name="code">@RestController
public class ReportController {
@RequestMapping(method = RequestMethod.GET, value = "/{reportId}")
public ResponseEntity getReport(@PathVariable String reportId) {
....
}
}
</pre>
As we can see, since controller class has no request mapping, and endpoint method is mapping with /{some_id}, when we try to hit /swagger-ui.html, the request is mapped to this method.<br />
<br />
After googling a lot, we ended up just making our request mapping more specific to a certain type of ids. In our case we are using mongo ids for the reports. It means ids can only have number or letters in lowercase.<br />
<br />
<br />
<pre class="java" name="code">@RestController
public class ReportController {
@RequestMapping(method = RequestMethod.GET, value = "/{reportId:^[0-9a-f]+$}")
public ResponseEntity getReport(@PathVariable String reportId) {
....
}
}
</pre>
<br />
Now because <b>swagger-ui.html</b> contains a dot (.) and a dash (-), the request is not being caught by this endpoint anymore.Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-23649082198870580812019-06-14T13:29:00.005-07:002019-06-14T13:32:43.976-07:00Save binary files in MongoDB with Spring-BootSaving binary files in MongoDB is pretty simple with Spring-Boot. You simply need to put <span style="font-family: "courier new" , "courier" , monospace;">byte[]</span> type in the <span style="font-family: "menlo"; font-size: 9pt;">CrudRepository</span> entity:<br />
<span style="background-color: white; font-family: "menlo"; font-size: 9pt;"><br /></span>
<span style="background-color: white; font-family: "menlo"; font-size: 9pt;"><br /></span><br />
<pre class="java" name="code">public interface BinayFilesRepository extends MongoRepository<BinaryEntity, String> {
}
@Document(BinaryEntity = "binaries")
public class BinaryEntity {
private String id;
private byte[] data;
}
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-18676673394135703422019-05-22T09:25:00.001-07:002019-05-22T09:25:08.298-07:00Docker: Run MySql ServerA practical way to start MySQL server using docker:<br />
<br />
<br />
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #000000}
span.s1 {font-variant-ligatures: no-common-ligatures}
</style>
<br />
<div class="p1">
<span class="s1">docker run -d -p 3306:3306 --name=mysql-server --env="MYSQL_ROOT_PASSWORD=123456" mysql --default-authentication-plugin=mysql_native_password</span></div>
<br /><br />
Then just connect outside the container with host:<br />
<span style="font-family: Courier New, Courier, monospace;">localhost, port:3306, user:root, password:123456</span>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-8876950141319005752019-02-25T09:19:00.004-08:002019-02-25T09:19:50.443-08:00Customize 'Read More' button text on slider for Wordpress themeI'm building a site using Wordpress and wanted to change the text in the slider buttons. The text says 'Read More' and since this is a Spanish site, I wanted to translate it. I'm a newbie in Wordpress and was surprised this wasn't something configurable in the theme or general site settings. Most of the solutions I found googling required to add a custom php function to override the text.<br />
<br />
At the end I found you can add custom css in the theme. So I used a css trick to change the content. First one hides the element and then uses the <b>after</b> selector in combination with the <b>content</b> css element.<br />
<br />
<br />
<pre class="css" name="code">.featured-link a span {
display: none;
}
.featured-link a:after {
content: 'Leer';
}
</pre>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUtZAdI8DjuqxYe-dsDZLPipjEdSvsSWQ2EHVZQuyyPY77zuPQ3HL3WlGsFM0ZQxBScuICxBs87xiK3MDoGCaDbI0VRjILS7zM2LxgUmYq0PCAuSBZDkb4FIaRTlC2cZBKSkMmXvAPR_Y/s1600/Screen+Shot+2019-02-25+at+11.14.21+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="617" data-original-width="1600" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUtZAdI8DjuqxYe-dsDZLPipjEdSvsSWQ2EHVZQuyyPY77zuPQ3HL3WlGsFM0ZQxBScuICxBs87xiK3MDoGCaDbI0VRjILS7zM2LxgUmYq0PCAuSBZDkb4FIaRTlC2cZBKSkMmXvAPR_Y/s640/Screen+Shot+2019-02-25+at+11.14.21+AM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-91352800605494784352019-01-11T08:30:00.000-08:002019-01-11T08:30:30.906-08:00AWS S3 Static Website 403 Forbidden error<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Got a 403 forbidden access error when trying to test my S3 static website. I set a policy to enable Get access to all resources but still something was wrong. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpIikv8rbInL4UkOFxo-DbGRG0PQb7fyEvZp7571s4bgQRSM3oXfMjK227hXkn5KxaisljPGJV7iw4CnPdO2EcuWlKBTB7Su0pSBocLRuTKcFSi1Sr0i1ZXNtCIW2kY_ZSvgbK13DimYg/s1600/Screen+Shot+2019-01-10+at+4.09.37+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="454" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpIikv8rbInL4UkOFxo-DbGRG0PQb7fyEvZp7571s4bgQRSM3oXfMjK227hXkn5KxaisljPGJV7iw4CnPdO2EcuWlKBTB7Su0pSBocLRuTKcFSi1Sr0i1ZXNtCIW2kY_ZSvgbK13DimYg/s640/Screen+Shot+2019-01-10+at+4.09.37+PM.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Exploring the security properties found this one: "Block public and cross-account access if bucket has public policies". I disabled it and finally was able to access site.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM8Zj5Ldm1YX_8Pw2QQ1-hYMrYHjTi_mC4PnyRmu2OlymLzdRBJtgHjuas5zWefXfSvxam1DCuuevCyaLG3OHzLG4s6UENmqhkyJUR56St_2cybCcEW5l3jrj3VVCjEQKHqikOpKHv2X8/s1600/Screen+Shot+2019-01-10+at+4.07.14+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="160" data-original-width="1444" height="70" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM8Zj5Ldm1YX_8Pw2QQ1-hYMrYHjTi_mC4PnyRmu2OlymLzdRBJtgHjuas5zWefXfSvxam1DCuuevCyaLG3OHzLG4s6UENmqhkyJUR56St_2cybCcEW5l3jrj3VVCjEQKHqikOpKHv2X8/s640/Screen+Shot+2019-01-10+at+4.07.14+PM.png" width="640" /></a></div>
<br />Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-25749242028162407122019-01-10T18:49:00.004-08:002019-01-10T18:49:46.191-08:00AWS S3 Static Website Hosting Error saving new Read Policy<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
I started an AWS tutorial in which first step is setting up a bucket to host a static website. I was trying to apply a policy to enable read access to all contents of bucket but was getting this error saving it: Error Access Denied. Not a very detailed message.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrO5JuFdJf9Uupvd6yKt5XWGyf2NM2QbhHAy1SZaBI70X2kOqvosv91xmiMGt7cDC0SW_qcd4l8H4Lw_Asa4OOJKIbVPIUb51Z3DS848mGmzCYBXzWFxpi5zCnrI77RU6y4RLrni80XA0/s1600/Screen+Shot+2019-01-10+at+3.42.35+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="703" data-original-width="1600" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrO5JuFdJf9Uupvd6yKt5XWGyf2NM2QbhHAy1SZaBI70X2kOqvosv91xmiMGt7cDC0SW_qcd4l8H4Lw_Asa4OOJKIbVPIUb51Z3DS848mGmzCYBXzWFxpi5zCnrI77RU6y4RLrni80XA0/s640/Screen+Shot+2019-01-10+at+3.42.35+PM.png" width="640" /></a></div>
<br />
I found that there is an option to "<b>Block new public bucket policies</b>" set in <b>true</b> by default.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtT0lKyr0nCjyg2uckJS8hxUaLpi7K7bRdKIQRzmbY1A0otVOT19uBoT40CtTVcIsq2iGxwVP3wOBHaSQM4V73uwuF73I2NuNnPvi5riE2WFT2BFX3C6eWLHcsY7DHKFS_Acvr4jwSjdw/s1600/block-new-policies.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="943" data-original-width="1600" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtT0lKyr0nCjyg2uckJS8hxUaLpi7K7bRdKIQRzmbY1A0otVOT19uBoT40CtTVcIsq2iGxwVP3wOBHaSQM4V73uwuF73I2NuNnPvi5riE2WFT2BFX3C6eWLHcsY7DHKFS_Acvr4jwSjdw/s640/block-new-policies.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
I set it to false and problem solved. I guess AWS wants to make sure no one enables full read access of the entire bucket by mistake.</div>
<br />Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-1715662067385964722018-08-14T14:12:00.001-07:002018-08-14T14:12:02.977-07:00Oracle Materialized View NotesCheck all the mviews:<br />
<br />
<pre class="code:sql">SELECT * FROM all_mviews;
</pre>
<br />
Refresh an mview:<br />
<br />
<pre class="code:sql">execute dbms_mview.refresh('my_cool_mview','f');
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-75427709805576612942017-08-01T14:38:00.000-07:002017-08-01T14:41:37.877-07:00Java Lambdas Tips<h2>
Creating a lookup method for an Enum</h2>
<br />
<br />
<h3>
Without lambdas:
</h3>
<pre class="brush:java">public enum Type {
INFO,
WARNING,
ERROR;
final static Map<String, Type> lookup = new HashMap<String, Type>();
static {
for(Type type : Type.values()) {
lookup.put(type.name().toLowerCase(), type);
}
}
public static Type of(String value){
Type val = lookup.get(value == null ? null : value.toLowerCase());
if(val == null){
val = ERROR;
}
return val;
}
}
</pre>
<h3>
With lambda:
</h3>
<br />
<pre class="brush:java">public enum Type {
INFO,
WARNING,
ERROR;
final static Map<String, Type> lookup = Arrays.stream(Type.values())
.collect(Collectors.toMap(t -> t.name().toLowerCase(), Function.identity()));
public static Type of(String value){
Type val = lookup.get(value == null ? null : value.toLowerCase());
if(val == null){
val = ERROR;
}
return val;
}
}
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-56159518441319661282017-01-11T07:51:00.001-08:002017-01-11T07:51:27.981-08:00Switching DNS in Ubuntu LinuxOne approach to switch among different development environments (QA, Integration, Stage, etc) is to have different DNS configs for routing one domain.<br />
<br />
In Ubuntu you can setup different network connections with different DNS servers and be able to switch from network top menu,<br />
<br />
1. Simply add a new network connection (may need two, one for Ethernet and one for WiFi)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9y-KLIANnxrihoBiWRN2hhHLXvOwcYFA2fikgrmSDfPaEzAm9BLuCvEAAd4OmS9_oe8Kd-Kh3oHk6jI08oWZqNPPPOUBc8Q7z_ElcrPUFXgQK5_irGAYqGHZOkU_gyEMglpbYA2NqQGk/s1600/Selection_185.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9y-KLIANnxrihoBiWRN2hhHLXvOwcYFA2fikgrmSDfPaEzAm9BLuCvEAAd4OmS9_oe8Kd-Kh3oHk6jI08oWZqNPPPOUBc8Q7z_ElcrPUFXgQK5_irGAYqGHZOkU_gyEMglpbYA2NqQGk/s320/Selection_185.png" width="320" /></a></div>
<br />
2. Configure DNS settings<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMNoTdLpPv8ardPpgMW9FaCIXNfNWdz-4-qHv2rxZ-vE-wXbeUTSwhxVecjX9Iz1SSYyqWoEpZR5Hw5n0bCx-_Q_xDnFQUtO6L1YTZir-Yvqs1ahjWIiXUtUp2tXANFV3t7F9m5e-d3bI/s1600/Selection_187.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMNoTdLpPv8ardPpgMW9FaCIXNfNWdz-4-qHv2rxZ-vE-wXbeUTSwhxVecjX9Iz1SSYyqWoEpZR5Hw5n0bCx-_Q_xDnFQUtO6L1YTZir-Yvqs1ahjWIiXUtUp2tXANFV3t7F9m5e-d3bI/s320/Selection_187.png" width="310" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
3. Switch to new connection from top menu</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghDp49o0RtEn2AwvoKJK-a64wwwo9Ds529ZDIhZeJd2hd8Y60_ySnA9tIHtGGJid9yCkYeg8pcsGNeqPfR7eUBzX_z6pCgkcHOVo-M41kA66MoKpoZA8TSvLMUx1fdAk-WcRcVK6IIphw/s1600/Selection_188.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghDp49o0RtEn2AwvoKJK-a64wwwo9Ds529ZDIhZeJd2hd8Y60_ySnA9tIHtGGJid9yCkYeg8pcsGNeqPfR7eUBzX_z6pCgkcHOVo-M41kA66MoKpoZA8TSvLMUx1fdAk-WcRcVK6IIphw/s1600/Selection_188.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
4. Test with <b>nslookup</b> command</div>
<br />Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-30271315286122750732016-03-11T08:42:00.004-08:002016-03-11T08:42:57.805-08:00Curl tips<h3>
Get response headers sending headers and cookies</h3>
<div>
<span style="font-family: Courier New, Courier, monospace;">curl --header "BB-App:androidNativeStore, 1.0.1" --cookie "lc=US|en" -I http://bitwise-api2.dev:8080/store-api/preferences/store-flag?store=us</span></div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">HTTP/1.1 302 Found</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Server: Apache-Coyote/1.1</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">X-Content-Type-Options: nosniff</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">X-XSS-Protection: 1; mode=block</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Cache-Control: no-cache, no-store, max-age=0, must-revalidate</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Pragma: no-cache</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Expires: 0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">X-Frame-Options: DENY</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">BB-Host: bitwise-api2.dev</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Location: https://store.bbcomcdn.com/deploy/images/common/flags/flag_us.jpg</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Content-Length: 0</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Date: Fri, 11 Mar 2016 16:41:41 GMT</span></div>
</div>
<div>
<br /></div>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-84661729518649687162015-10-18T19:01:00.002-07:002015-10-18T19:01:46.697-07:00Oracle Troubleshooting: ORA-01114, ORA-01110, ORA-27091, ORA-27041During troubleshooting on an issue in Oracle DB I was getting these errors:<br />
<br />
<pre>sqlplus / as sysdba
SQL> startup
ORACLE instance started.
Total System Global Area 1068937216 bytes
Fixed Size 2233344 bytes
Variable Size 809503744 bytes
Database Buffers 251658240 bytes
Redo Buffers 5541888 bytes
Database mounted.
ORA-01114: IO error writing block to file 5 (block # 1)
ORA-01110: data file 5: '/data/oracle/bb/bb1.dbf'
ORA-27091: unable to queue I/O
ORA-27041: unable to open file
Linux-x86_64 Error: 13: Permission denied
Additional information: 3
</pre>
<br />
I changed the permissions to data file and problem solved:
<br />
<pre>
</pre>
<pre>sudo chmod 660 /data/oracle/bb/bb1.dbf
sqlplus / as sysdba
SQL*Plus: Release 11.2.0.2.0 Production on Fri Oct 16 17:59:36 2015
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
SQL> startup
ORACLE instance started.
Total System Global Area 1068937216 bytes
Fixed Size 2233344 bytes
Variable Size 809503744 bytes
Database Buffers 251658240 bytes
Redo Buffers 5541888 bytes
Database mounted.
Database opened.
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com1tag:blogger.com,1999:blog-8717990482817180089.post-56024609745491366342015-09-18T19:39:00.002-07:002015-09-18T19:40:34.106-07:00ATG QueryBuilder: How to create nested queries / subqueriesI had to deal a good amount of time finding a way to query repositoy items that would match some relationships among different item descriptors. I was trying to look for a way to perform a join over different item descriptors, but at the end was able to solved it in a nested queries fashion. You know,<br />
<br />
the typical thing in SQL where you filter by matching a column from the result of another query:<br />
<br />
<pre class="brush:sql">SELECT column_name [, column_name ]
FROM table1 [, table2 ]
WHERE column_name OPERATOR
(SELECT column_name [, column_name ]
FROM table1 [, table2 ]
[WHERE])
</pre>
<br />
This in ATG can be done using a query builder operation called: <a href="http://docs.oracle.com/cd/E23507_01/Platform.20073/apidoc/atg/repository/QueryBuilder.html#createIncludesItemQuery(atg.repository.QueryExpression, atg.repository.Query)" target="_blank">createIncludesItemQuery</a>. You can pass as parameter another query and the colum matching expression.<br />
<br />
<br />
<pre class="brush:java">
// Get guide items where tabs in the guide matches some other tabs query
RepositoryItemDescriptor guideRepositoryItemDescriptor =
getProductRepository().getItemDescriptor("guide");
RepositoryView guidesRepositoryView = guideRepositoryItemDescriptor.getRepositoryView();
QueryBuilder guidesQueryBuilder = guidesRepositoryView.getQueryBuilder();
QueryExpression tabsPropertyExpression =
guidesQueryBuilder.createPropertyQueryExpression("tabs");
// productTabsQuery is another query built
Query productGuidesQuery =
guidesQueryBuilder.createIncludesItemQuery(tabsPropertyExpression, productTabsQuery);
RepositoryItem[] guides = guidesRepositoryView.executeQuery(productGuidesQuery);
</pre>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-51948843175737269862015-06-26T13:39:00.004-07:002015-06-26T13:45:05.621-07:00Reactive Programming: Reactive Extensions Library (Starting to learn this reactive thing)<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/dwP1TNXE6fc/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/dwP1TNXE6fc?feature=player_embedded" width="320"></iframe></div>
<br />
<br />
<div style="text-align: justify;">
I found a great video explanation of Reactive Programming in the flavour of <a href="http://reactivex.io/">Reactive Extensions Library</a> done by Jafar Husain (Technical Lead at Netflix) . Here's a brief summary of the main concepts I extracted.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://i.ytimg.com/vi/hLYMD6R6PvU/hqdefault.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="Event Reacting" border="0" src="http://i.ytimg.com/vi/hLYMD6R6PvU/hqdefault.jpg" height="240" title="Event Reacting" width="320" /></a></div>
<div style="text-align: justify;">
The term Reactive can be associated by analogy with someone throwing a lot of balls at you at the same time. You need to react quickly to try to catch them all. You don't have control over when you want the balls to be thrown at you, you just need to be prepared to catch them.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
To start understanding Rx (Reactive Extensions), we need to have two basic design patterns in consideration: Iterator and Observer.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
In Java we have an <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html">Iterable</a> interface, which is an interface any type of collection can implement to allow consumers of a collection obtain items one at the time. That's how in Java we can use the <b>foreach</b> operator to traverse a collection. Because the interface provides a specific <a href="http://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html">Iterator</a> for the data type we are consuming in the collection. Three things can happen when using an iterator:</div>
<ol>
<li>Get the next item</li>
<li>No more items to consume (end of the data stream)</li>
<li>An error can happen (using exception throwing in languages like Java)</li>
</ol>
<div style="text-align: justify;">
The Observer pattern is another well known design pattern where suscribers get suscribed to subjects in order to be notified when a change occurs in the subject. If we analize this pattern, we can find that it's very similar to the Iterator pattern in the sense that there is a producer and a consumer. Main difference is that in the Observer the producer is in control for when sending the data, while in the Iterator the consumer is in control. He decides when to pull the data from the producer.</div>
<div>
<br /></div>
<div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiEgXu1JRaXOua9BvR9Yqfei4T4Qfa0OggOnS82gdFtE3qPHxNzZ-wv9K9JTV2at9FEq_p-eViAjPMwK0e4cKimFUjqJAKYacntMRvnb9dTRbjeJMfUxskobbSQFn0EfxtP_3EN19jpXY/s1600/fusion-iterator-observer.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em; text-align: justify;"><img alt="Iteratot Observer Fusion" border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiEgXu1JRaXOua9BvR9Yqfei4T4Qfa0OggOnS82gdFtE3qPHxNzZ-wv9K9JTV2at9FEq_p-eViAjPMwK0e4cKimFUjqJAKYacntMRvnb9dTRbjeJMfUxskobbSQFn0EfxtP_3EN19jpXY/s320/fusion-iterator-observer.png" title="Iteratot Observer Fusion" width="320" /></a><br />
<div style="text-align: justify;">
But there are two main things "missing" from the Observer pattern that are present in the Iterator:</div>
</div>
<div>
<ul>
<li style="text-align: justify;">A way to indicate there is no more data</li>
<li style="text-align: justify;">A way to indicate an error ocurred</li>
</ul>
<div style="text-align: justify;">
In the Observer you can only suscribe callback to receive data, but you cannot register callbacks for the event where is indicated no more data is going to be pushed (completion event), or to indicate an error happened. Reactive extensions is basically about unifying the observable pipe with the iterable pipe producing a new type called the Observable. It gives the same semantics of both the Iterable and the Observable.</div>
</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
There are a lot of interesting operations that can be done over iterables. Similar to SQL where there are a lot of operations that can be done over sets: filter, select, order, etc. What about if all the same things we can do over data sets residing in a table, could also be done over events (data arriving in). That is not a dream with Rx. It is possible to write SQL style queries over events. The difference is that the query is evaluated as data arrives. You can evaluate data in real time.</div>
<div style="text-align: justify;">
<br /></div>
<div>
<div style="text-align: justify;">
More and more code is becoming evented. We have a lot of asynchrouns calls both in client side (JS), and server side (Node.JS). That adds a lot of complexity in the application trying to handle all these</div>
<div style="text-align: justify;">
callbacks. The reactive extensions library provides this new Observable data type that establishes a powerful way to model events. By completing the missing semantics (end of data stream and error), you can now apply operations familiar in collections like map, filter, reduce, merge, zip, etc. All those things that could be done over data streams we can pull, now can be done over streams of data we can push.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Imagine the new <a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html">Java 8 Stream API </a>over data that is arriving dynamically over events pushed to you. Lot of things can improve in an application like serving faster data to consumers just to cite an example.<br />
<br />
Still need more learning for this new paradimg, but at least I think this video covers fundamental concepts we need to have before getting our hands into the code.</div>
</div>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-67153835871670434312015-06-05T15:01:00.004-07:002015-06-05T15:08:22.585-07:00Apache2: Configuring a local page with custom host name (Virtual Host)1. Create a folder /wwww/domain<br />
<span style="font-family: Courier New, Courier, monospace;">sudo mkdir -p /www/domain </span><br />
<br />
2. Change permissions<br />
<span style="font-family: Courier New, Courier, monospace;">sudo chown 755 /www/domain
sudo chown -R www-data:www-data /www/domain </span><br />
<br />
3. Edit Apache2 conf<br />
<span style="font-family: Courier New, Courier, monospace;">sudo gedit /etc/apache2/apache2.conf
Add these lines:
</span><br />
<pre class="brush:xml"><VirtualHost *:80>
# This first-listed virtual host is also the default for *:80
ServerName gabo.com
ServerAlias gabo.com
DocumentRoot /www/domain
</VirtualHost>
<Directory /www/domain/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</pre>
<br />
4. Add DNS line in /etc/hosts<br />
<span style="font-family: Courier New, Courier, monospace;">127.0.0.1 gabo.com </span><br />
<br />
5. Create index file<br />
<span style="font-family: Courier New, Courier, monospace;">touch /wwww/domain/index.html
add HTML code:
</span><br />
<pre class="brush:html"><http>
<body>
<h1>Hello!</h1>
</body>
</http>
</pre>
<br />
6. Open domain gabo.com in browser.Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-4300005369587258632015-05-25T15:12:00.001-07:002015-05-25T15:12:43.282-07:00JSON Jackson custom deserializer to return empty String instead of nullNeeded a way to deserialize null values in a JSON as emptry strings (""). This is the shortest I could come with.<br />
<pre class="brush:java">import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class NullHandlerDeserializer extends UntypedObjectDeserializer {
private static final long serialVersionUID = 1L;
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
switch (jp.getCurrentToken()) {
case VALUE_NULL:
return "";
default:
return super.deserialize(jp, ctxt);
}
}
}
</pre>
<br />
The class extends from an existing deserializer implementation to avoid having to code the handling of all JSON tokens. I needed to worry only about VALUE_NULL token.
<br />
<br />
And to configure the custom deserializer in the ObjectMapper:<br />
<br />
<pre class="brush:java">ObjectMapper om = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Object.class, new NullHandlerDeserializer());
om.registerModule(module);
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-17706900473534983522015-05-20T15:18:00.000-07:002015-06-05T14:48:13.842-07:00XSLT LearningsJust listing some XSLT learnings from a recent project where I had to learn from scratch.
<br />
<h2>
Basic shelling:</h2>
<pre class="brush:xml"><?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<!-- XSL stuff -->
</xsl:template>
</xsl:stylesheet></pre>
<h2>
Converting a list of states from XML to HTML select input options: </h2>
<h3>
Given an input like this:</h3>
<pre class="brush:xml"><states>
<AL>Alabama</AL>
<AK>Alaska</AK>
<AR>Arkansas</AR>
<CA>California</CA>
<CO>Colorado</CO>
</states>
</pre>
<br />
<h3>
Use XSL like this:</h3>
<pre class="brush:xml"><select name="states">
<xsl:for-each select="states/*">
<option>
<xsl:attribute name="value"><xsl:value-of select="name(.)"/></xsl:attribute>
</option>
<xsl:value-of select="string(.)"/>
</xsl:for-each>
</select>
</pre>
<h3>
To get this:</h3>
<pre class="brush:html"><select>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
</select></pre>
<h3>
Some few things to note:</h3>
<ul>
<li>"name(.)" gives you the element (tag) name.</li>
<li>"string(.)" gives you the element value (what is between opening and closong tags).</li>
</ul>
<h3>
<br /></h3>
<h3>
Checking element has children or content</h3>
<pre class="brush:html"><xsl:if test="some/element-tag/text() != ''">
...
</xsl:if>
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-84683664597911764142014-12-12T14:28:00.000-08:002014-12-12T14:31:23.423-08:00Spring Web: Get current request and generate base URLJust a simple way to generate application's base URL according to the environment context using current request:<br />
<br />
<br />
<pre class="brush:java">protected static String getBaseEnvLinkURL() {
String baseEnvLinkURL=null;
HttpServletRequest currentRequest =
((ServletRequestAttributes)RequestContextHolder.
currentRequestAttributes()).getRequest();
// lazy about determining protocol but can be done too
baseEnvLinkURL = "http://" + currentRequest.getLocalName();
if(currentRequest.getLocalPort() != 80) {
baseEnvLinkURL += ":" + currentRequest.getLocalPort();
}
if(!StringUtils.isEmpty(currentRequest.getContextPath())) {
baseEnvLinkURL += currentRequest.getContextPath();
}
return baseEnvLinkURL;
}
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-38181684358789801542014-11-04T11:04:00.000-08:002014-11-04T11:04:13.291-08:00DOS: List files recursivelyTo list files recursively in Windows you don't need a complex script as I thought. For example to list all .html inside subfolders you can just run simple DOS command:<br />
<br />
<b>dir /s/b *.html > results.txt
</b>Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-37232584708840950242014-10-15T16:36:00.001-07:002014-10-15T16:37:44.696-07:00Ant: adding additional JARs to classpath<div style="text-align: justify;">
I was working in a custom ATG module in which I required to extend a core ATG class. When I ran the build ant command I got compile errors because of "<b>cannot find symbol</b>", in other words, my dear reference class was not being recognized. It was missing! In my Eclipse project I got no errors because I had the right library included in the project's classpath.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
I started looking on how to add additional JAR files to the classpath, but after digging a little bit more in the compile target referenced in the build.xml:</div>
<br />
<pre class="brush:xml"><target name="build" depends="echo-build-message,clean,compile,jar-classes,jar-configs,copy-to-install" />
</pre>
<br />
I saw how could I add additional classpath entries using the reference id "classpath.additions". In the included common.xml file we had this:<br />
<br />
<br />
<pre class="brush:xml">
<target name="compile">
<mkdir dir="${java.output.dir}"/>
<mkdir dir="${java.src.dir}"/>
<copy todir="${java.output.dir}">
<fileset dir="${java.src.dir}">
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
<if>
<isreference refid="classpath.additions" />
<then>
<path id="fullClasspath">
<path refid="classpath" />
<path refid="classpath.additions" />
</path>
</then>
<else>
<path id="fullClasspath">
<path refid="classpath" />
</path>
</else>
</if>
<echo message="java.src.dir: ${java.src.dir}, java.output.dir: ${java.output.dir}" />
<javac srcdir="${java.src.dir}" destdir="${java.output.dir}" classpathref="fullClasspath" debug="on" includeAntRuntime="false" />
</target>
</pre>
<br />
So I just added this in the build.xml:
<br />
<pre class="brush:xml">
<path id="classpath.additions">
<fileset dir="${dynamo.home}/../REST/lib"><include name="**/*.jar" /></fileset>
</path>
</pre>
This seems like an elegant generic way for configuring classpaths in a multi module environment like ATG.
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-51708023785621019912014-10-14T13:14:00.000-07:002014-11-19T09:37:04.048-08:00ATG: Nucleus tips<h3>
Get current request URI</h3>
<pre class="brush:java">import atg.servlet.ServletUtil;
//...
ServletUtil.getCurrentRequest().getRequestURI();</pre>
<br />
<h3>
Define a collection of components</h3>
<div>
<b>Java</b>:</div>
<pre class="brush:java">
protected Component [] componentes;
public void setComponents(Component [] components) {
this.components = components;
}
public Component [] getComponents() {
return components;
}
</pre>
<div>
<br /></div>
<div>
<b>Properties</b>:</div>
<pre>components=\
/path/to/some/Component,\
/path/to/another/Component
</pre>
<div>
<br /></div>
<div>
<br /></div>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com0tag:blogger.com,1999:blog-8717990482817180089.post-70846166383521384272014-07-22T13:38:00.001-07:002018-07-10T13:20:45.494-07:00Spring: Integration testing under Spring Boot application context but mocking services<br />
Use <b>@MockBean</b> annotation<br />
<br />
<span style="color: red;">DEPRACATED:</span><br />
<br />
<span style="font-size: xx-small;">So I wanted to create some integration tests for an API application that uses Spring Boot. The thing is that I needed to load the same application context as the real applicationm, because Spring Boot performs a bunch of configurations for you, and these were not being loaded in a StandAlone mode.</span><br />
<span style="font-size: xx-small;"><br /></span>
<span style="font-size: xx-small;">But also I didn't want to use real services which depend upon real data. Depending on databases for your tests creates fragile tests.</span><br />
<span style="font-size: xx-small;"><br /></span>
<span style="font-size: xx-small;">After researching on the web I found this way:</span><br />
<span style="font-size: xx-small;"><br /></span>
<span style="font-size: xx-small;"><br /></span>
<span style="font-size: xx-small;">This is the typical Spring-Boot entry point class.
</span><br />
<br />
<pre class="brush:java">i<span style="font-size: xx-small;">mport org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@EnableAutoConfiguration
@ComponentScan
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
</span></pre>
<span style="font-size: xx-small;">A base class having the logic to init the application context.</span><br />
<span style="font-size: xx-small;"><br /></span>
<pre class="brush:java"><span style="font-size: xx-small;">import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = MyApplication.class)
public abstract class BaseIntegrationTest {
protected MockMvc mockMvc;
@Autowired
protected WebApplicationContext wac;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = webAppContextSetup(wac).build();
}
}
</span></pre>
<span style="font-size: xx-small;">An implementation class mocking a required service class.</span><br />
<pre class="brush:java"><span style="font-size: xx-small;">public class FooIntegrationTest extends BaseIntegrationTest {
@Configuration
public static class TestConfiguration {
@Bean
@Primary
public FooService fooService() {
return mock(FooService.class);
}
}
@Autowired
FooService mockFooService;
}</span>
</pre>
Gabriel Solanohttp://www.blogger.com/profile/15936026176587418957noreply@blogger.com2