Saturday, October 13, 2018

Setting environment variable from code in Java

Normally the environment variables are set, as the name implies, but the environment. However, sometimes a code runs as part of some environment that you cannot control.
Many times it happens as part of integration or performance tests.

And here's the way to overcome it:

void setEnv(String name, String value) {
try {
final Class<?> envClass = Class.forName("java.lang.ProcessEnvironment");
final Class<?> uMapClass = Class.forName("java.util.Collections$UnmodifiableMap");
final Field mField = uMapClass.getDeclaredField("m");
mField.setAccessible(true);
final Field theUnmodifiableEnvironment = envClass.getDeclaredField("theUnmodifiableEnvironment");
theUnmodifiableEnvironment.setAccessible(true);
final Map oldMap = (Map) theUnmodifiableEnvironment.get(System.getenv());
final HashMap newMap = new HashMap(oldMap);
newMap.put(name, value);
mField.set(System.getenv(), newMap);
} catch (Exception e) {
e.printStackTrace();
}
}


Wednesday, November 15, 2017

Why you should be careful when using Lombok or other code generation tools

Look at the following code:

public class LombokTest {
@Test
public void testHashCode() {
final HashSet<Object> set = new HashSet<>();
final MyEntity myEntity = new MyEntity("abc", "edf", "hhh");
assertTrue(set.add(myEntity));
myEntity.setSomethingElse("gggg");
assertTrue(set.contains(myEntity));
}
@AllArgsConstructor
@Data
public static class MyEntity {
private String id;
private String something;
@Setter
private String somethingElse;
}
}
view raw LombokTest.java hosted with ❤ by GitHub


Will the testHashCode succeed or fail?

The answer it will fail, since the second assert: assertTrue(set.contains(myEntity)) will not find myEntity in the HashSet.
But it's there, right? It was never removed.

So what we have here is: 1. Business problem, since it's impossible to get object from set that is there. 2. Memory leak.

But how did it happen?

The problem is with Lombok's @Data annotation. It's a very convenient annotation that auto-generates all methods the java utility methods: equals, hashCode, toString as well as relevant constructors and getters.
Yes, it's very convenient, but a hidden problem is introduced: equals and hashCode include all fields and when value of a field changes, the hashCode returns a different value. Therefore, the object cannot be found in HashSet anymore.

This problem is not unique to Lombok. Exactly the same problem will occur if you write the method yourself by using mutable fields or if you use any other code-generation or reflection tools.
However, if you write code yourself, it's a bit more visible, while with Lombok it's kind of woodoo.

The best practices here are not related to Lombok or other library and are quite simple:
1. As much as possible try to make your class immutable.
2. Even if a class is mutable, are all fields mutable? Use only immutable fields in hashCode and equals and you will be safe.
3. If a class is completely mutable and you cannot rely on some immutable fields, reconsider if you need to override hashCode and equals at all. Is default implementation sufficient?
4. If none of above doesn't work for you - document. Put a HUGE WARNING in javadoc explaining why the users of the class must be careful, if they decide to store the instances in HashSet or as a key in a HashMap.

Friday, October 20, 2017

Running arbitrary command when using auto-completion in Zsh

Did you ever wanted to run an predefined command as auto-completion in zsh?
For example, you may have a script that accepts only predefined values as input.
Actually it's possible and quite easy.

First, you will need a file that will be invoked to auto-complete the command.
Here's an example:
#compdef mycommand
_mycommand() {
local -a commands
commands=(
$(HERE_COMES_SHELL_COMMAND)
)
if (( CURRENT == 2 )); then
_describe -t commands 'commands' commands
fi
return 0
}
_mycommand
view raw _custom hosted with ❤ by GitHub

You can replace the HERE_COMES_SHELL_COMMAND with any command. For example it can be "cat ~/myfile" to read options from a file.
#compdef defines the list of commands this file will auto-complete. In the above example it's going to be mycommand, change it to your actual command.
As I already mentioned, it can be also a list: #compdef mycommand myscript myprogram - will autocomplete any of the mycommand, myscript, myprogram.

Now, you need to tell zsh about your file:
1. Place your file to some directory. For example: ~/.myautocomplete
2. In your ~/.zshrc add the following line: fpath=(~/.myautocomplete $fpath)
3. After this line add the following lines:
autoload -U compinit
compinit

On MAC I used the following instead:
autoload -U compaudit compinit

4. You may need to delete files that start with .zcompdump in your home directory.
5. Start a new shell, and it should work.

If you are using oh-my-zsh, it's a bit easier:
1. Create your directory under ~/.oh-my-zsh/plugins
For example ~/.oh-my-zsh/plugins/myautocomplete
2. Place this file in this directory.
3. Edit ~/.zshrc, find "plugins" and add "myautocomplete" to the list.
4. Start a new shell.

Tuesday, October 10, 2017

How do I avoid the error "Unable to validate the following destination configurations" when using S3 event notifications in CloudFormation?

There is a very important post about avoiding the "Unable to validate the following destination configurations" in AWS.
Too bad it's not mentioned just next to both S3 and SNS/SQS reference documentation.

BUT! This post is lacking some important part: You will get this error even if you didn't specify the TopicPolicy (or QueuePolicy) at all!
Furthermore, you will get this error even if you specified the policy, but it's not correct.
For example, if your policy is too restrictive and S3 would not be able to send events to SNS, you will also get this error! Is it clear from error's description? Not really. Is it clear from the above's AWS post? No, not at all.

So just remember, when you see "Unable to validate the following destination configurations" - check the Policy. It may be lacking. It may be too permissive or too restrictive, but the problem is with the policy, and not with a bucket.

Wednesday, October 4, 2017

CloudFormation Tips

Some tips of using the CloudFormation:

1. Don't specify a resource name, unless absolutely must doing so. This way you can avoid names clashes, since CloudFormation will automatically assign unique names to your resources.
2. If you need to specify a name, include the stack name in it. This way you will reduce the potential name clashes. You can also include a partition and region for resources that are available globally (e.g. S3 bucket names). Note that this will NOT prevent the potential naming clash completely, since somebody else can also use the same name.
3. When creating any IAM resources in your stack, make sure to add DependOn in the resources that use these IAM resources. Apparently CloudFormation is not smart enough to resolve this dependency tree and handle it without additional configuration.
4. Sometimes the names the CloudFormation will give to your resources is completely unrelated to the stack name. Include the ARN of such resources in the Outputs, so you can easily find them later, when needed.
5. Very common scenario in AWS is a S3 bucket that fires events to SNS or SQS, when a file is uploaded. Apparently it's impossible to create it in single change. See this post.


See also:

# When using parameters, use AWS predefined types when applicable
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
Parameters:
# The user will be able to choose from the list of available subnets
Subnets:
Description: List of subnets
Type: List<AWS::EC2::Subnet::Id>
# The user will be able to choose from the list of available security groups
SecurityGroup:
Description: List of Security guides
Type: AWS::EC2::SecurityGroup::Id
# Pseudo parameters are parameters that are predefined by AWS CloudFormation
# Looks at AWS::StackName in the example below
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html
AWSBatchFullAccessPolicy:
Type: "AWS::IAM::Policy"
Properties:
PolicyName: !Join ["_", [!Ref "AWS::StackName", "AWSBatchFullAccess"]]
view raw cf.yml hosted with ❤ by GitHub

Sunday, April 10, 2016

Print Gradle Dependencies

One way to print the project's Gradle dependencies is 'gradle dependencyReport'.
However it creates a very large file with many scopes that are sometimes hard to track.
Sometimes it can be useful just to print the list of dependencies of a specific scope.
A very small script can do the job, and here are some examples:

// Runtime
task printRuntimeDependencies << {
project.configurations.runtime.each {
file -> println(file)
}
}
// Compile
task printCompileDependencies << {
project.configurations.compile.each {
file -> println(file)
}
}
// Compile SubModule
task printCompileDependencies << {
project(':main').configurations.compile.each {
file -> println(file)
}
}
view raw build.gradle hosted with ❤ by GitHub

Monday, November 23, 2015

Dropwizard: Add thread name to log

This should have been trivial, but somehow it isn't.
So I'll put it here.
Adding the thread name to Dropwizard logs:

#note the logFormat
logging:
level: INFO
appenders:
- type: console
threshold: INFO
timeZone: "UTC"
logFormat: "%-5p [%d{ISO8601,UTC}] %c: %m [%t]%n%rEx"
- type: file
threshold: ALL
timeZone: "UTC"
logFormat: "%-5p [%d{ISO8601,UTC}] %c: %m [%t]%n%rEx"

Unix Shell: Use of functions to create complicated aliases

In *nix shells it sometimes useful to create aliases that receive parameters.
This can be done using functions:

# ssh with password and without host signature checking
function pssh() {/usr/bin/sshpass -p PASSWORD /usr/bin/ssh -oStrictHostKeyChecking=no USERNAME@$1}
# ssh with private key
function kssh() {/usr/bin/ssh -oStrictHostKeyChecking=no -i ~/.ssh/key.pem USERNAME@$1}
view raw .zshrc hosted with ❤ by GitHub


Now you can type just something like: kssh 192.168.1.1 or pssh 192.168.1.2

Sunday, January 18, 2015

Pretty Format of JSON in vim

Open ~/.vimrc
Add the following line:
command Json :%!python -m json.tool

To format Json, type :Json
Note: you need Python installed.

Inspired by this post.

Wednesday, May 14, 2014

Monitor Java on Unix/Linux/Solaris

Just a short memo of useful commands that can help to troubleshoot Java on *nix:
(Btw, most of them will also work on Windows, but who runs Java on Windows? Just kidding...)

jps -m - show running Java processes and their pids.
jstack <pid> - print thread dump
jmap -dump:format=b,file=<path to file> <pid> - save heap dump to file