Saturday, May 24, 2014

Thinking about Java 8

With all the fanfare of the impending Java 8 release, I thought it would be a good opportunity to brush up on some of the new features and think about how useful they might be at work.   Here's what I've come up with so far:

  • @FunctionalInterface - I like this as it allows me to lock down interfaces that I want to have only one method (which is what makes them functional, or function-like).   I know a co-worker or two who will really like this.
  • java.util.time - Finally!   JODA time users (like me) will find this to be very familiar looking.
  • Lambdas - I think any Groovy user will say "finally, something like groovy closures!".   This will probably come in handy, but...
    1. As with anything concise and powerful, it could be misused.  Golden hammer problems might happen (suddenly everything has to be a Lambda).
    2. The syntax is close to what Groovy does, so it might be a little confusing to those of us who switch back and forth between Groovy and Java.
    3. The combination of Lambdas and function/method reference shorthand can result in some very 'tight' code.
     
  • No more Permanent Generation -  Okay, so now classes, interned strings and static fields are in the existing 'old' generation?   Sounds good to me initially, since I'm a big fan of 'one kind of stuff'.   However, I'm not sure about how this will affect GC configurations such as the one that I use frequently at work (ParNew + CMS).
 These features reduce the gap between Java and Scala.   As good as Scala is, it's not easy to justify using it in many cases, and with Java 8, I think that set of cases got quite a bit smaller.   I'll probably learn Scala anyway, just because, but for production code I'm thinking Java 8 would be a safer bet.

Friday, May 23, 2014

Job Hunting - Networking and Recruiters

I figured I'd write about my job search here, since that's part of being a software engineering world.   Hopefully some people will benefit from some of my experiences.

  1. Keep a list of opportunities - Company name, position, status (applied, first interview, etc.) any notes about each interview.   This will come in handy when talking to your network or recruiters.   I use a Google apps spreadsheet for this.   Keep the active jobs at the top, and the 'no' list at the bottom.
  2. Use your network - Don't be afraid to reach out to your industry friends and former co-workers.   I used to feel that this was kind of... 'cheating', but that is a big mistake.   These are people that know you, that have worked with you.   You don't need to convince them of anything really.   Your friends will be happy to help you out if they can.    You would do the same for them, wouldn't you?   If they don't have anything suitable at their companies, maybe someone they know will.
  3. Go to some tech meetups in your area - In addition to maybe learning about some new things, it's a great way to meet other technical people.  Often, if a company is hiring they will encourage their engineers to go to these events and look for talent.   It might be a good idea to print up some personal business cards to hand out.
  4. Use recruiters to gain access to other opportunities - A good recruiter will have access to some opportunities that you may not know about.   They will also handle the interview scheduling, and give you more insight into the structure of the hiring company.   When you're interviewing through your network, you have to do all this yourself.  
    • Make sure the recruiter lets you know about any job before sending your resume anywhere.   Check your list to make sure you haven't already applied.
    • Remember, recruiters are getting paid by the hiring company, usually as a percentage of the yearly comp.   So, they will put much more effort into a senior level position than any junior position.
  5. Filter the opportunities, especially when going through your network - If the hiring manager requires certain technical skills that you don't have, don't just send send your resume.   If the job sounds really interesting, but your skills are not a great match, maybe a short conversation with the hiring manager is in order.   Sometimes, the hiring company wants to hire "good people" who can learn the technology specifics.   Other times, the company really wants something very specific (which IMO is a bit of a red flag), so if that's the case don't waste everyone's time by applying.
  6. Filter recruiters - If a recruiter is not showing you anything exciting, isn't efficient at scheduling interviews, or doesn't prepare you well for the interviews then move on.   No point in wasting time.  

Tuesday, April 22, 2014

Upgrading Fedora - Notes

A few notes on upgrading Fedora installations.



Fresh Install 

Probably the safest way to get a working upgrade is to back up any home directories or important configurations and go with a fresh installation.

Upgrades often leave undesirable configurations in home directories (GNOME configs, for example).  This often leads to strange desktop / display issues that can't easily by found or fixed.

You are not using OSX here.   Migrating settings and applications may or may not work.  :)

1. Create a USB Stick

On Fedora 19, these instructions didn't work for me.   Here is what I ended up doing.  Get a USB stick that doesn't have anything important on it.
  1. Download the ISO image.
  2. Insert the USB stick.
  3. Start the Disks application and select the USB drive in the left panel. 
  4. Unmount the USB disk filesystem if it is mounted. 
  5. Up at the top of the right panel, click on the gear icon and select Restore Disk Image.
  6. Select the downloaded ISO image file, and click Start Restoring.... 

2. Boot using the USB Stick, complete the installation

Shut down the machine, and re-start.   If you need to, use the BIOS to select the USB as the boot drive. 

Go through the install process.   Best to dedicate a HDD to the install, that way you can boot from that drive via BIOS boot selector if you want multiple OS's on your computer without too much hassle.   In my case, I've got a dual boot workstation, with a HDD dedicated to booting Linux.   I use the BIOS boot drive selector to boot up Fedora instead of WinDoze. 

NOTE: I've found that EZbcd doesn't play nice with UEFI boot partitions that Fedora 20 installs.    Best to just use the BIOS to select a boot disk.

 

 

 

Using FedUp

WARNING, THIS DOESN'T ALWAYS WORK.   Almost every time I've done this, there were some strange after-effects with GNome at least.

For newer versions of Fedora (newer than 17), FedUp with the network upgrade is the way to go:

$ sudo yum install fedup
$ sudo yum update fedup fedora-release
$ sudo fedup --network 20

Where 20 is the version you want to upgrade to.  Fedup will automatically reboot the system when it's done downloading everything.


The Fedora site says: "Prior to Fedora 17, the DVD/ISO/USB Drive option is recommended."

Yeah, well... what they really mean is, that FedUp will probably get you something that boots and runs some things, but you may discover later on that many settings are just plain broken.


Tuesday, April 8, 2014

MySQL - Making Snapshots and Loading Snapshots

Just a quick note on how to make database snapshots with MySQL.

Create a compressed snapshot:

$ mysqldump --single-transaction -udbuser -pdbpass somedb | bzip2 > somedb.sql.bz2

  • The --single-transaction option can be left out if you are not using InnoDB.
  • In newer versions of MySQL/Moriadb, --opt is the default, so there's no need to specify it.
Load a compressed snapshot:

$ bunzip2 -c somedb.sql.bz2 | mysql -u dbuser -pdbpass somedb 

These commands are usually best done as a background job, as they can take some time to complete. Also, they may cause long delays for any applications using the database, so it's a good idea to shut the application servers down before creating a snapshot.

Monday, March 31, 2014

Seam 2 Gotchas - Part 1

A few common mistakes I've seen made with Seam 2:

Referencing EJB Components Directly

I think it's pretty easy to know that referencing a Seam component that happens to be an EJB directly (with @EJB or with a JNDI lookup) is probably not going to end well, but a novice Seam developer might make this mistake.  This mistake is more likely when writing code in a non-JSF part of the system (e.g. a Servlet).

Here's what you can do about it:
  1. Make sure EJB Seam components are injected using @In, or look them up using Seam.   Make sure you have a clear distinction between 'regular' EJBs and Seam component EJBs.  Here are the two main things to avoid:    
    • INJECTING SEAM COMPONENTS WITH @EJB - Use @In instead!
    • LOOKING UP SEAM COMPONENTS WITH JNDI - Use Component.getInstance(name) or  Contexts.lookupInStatefulContexts(name) instead!
  2. In a non-JSF environment, use ContextualHttpServletRequest, for example, in a Servlet:
        @Override
        protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
        {
            //... do some stuff...
            new ContextualHttpServletRequest(request) 
            {
                @Override
                public void process() throws Exception
                {
                    // Access the components, do work.   The contexts will be properly set up here.
                    MyComponent component = (MyComponent)Component.getInstance("myComponent");
                }
            }.run();    // Run the request.
     
        }
    
    See this JBoss community post.   Personally, I strongly prefer using ContextFilter.
  3. In a non-JSF environment, apply the ContextFilter.   This will automatically wrap all requests in ContextualHttpServletRequest().

    For example:  <web:context-filter url-pattern="/servlet/*"/> in components.xml.
Note that when using ContextFilter or ContextualHttpServletRequest, exceptions may be handled differently than you might expect!

If anything inside the ContextFilter/ContextualHttpServletRequest throws an exception, then all the contexts will be torn down.   You may get other filters throwing java.lang.IllegalStateException: No active event context! after the ContextFilter/ContextualHttpServletRequest has finished!

Component Lookup - Component is not automatically created?

While injecting Seam components with @In is the simplest way to access another component, there are cases where a lookup is needed (e.g. in a Servlet).   The problem is, there is more than one way to look up components, and the method used to look up the component will determine the behavior:

  1. Contexts.lookupInStatefulContexts(name) - This is similar to @In : It will not create components automatically!
  2. Component.getInstance(name) - This is similar to @In(autocreate=true) : Components will be created automatically if they don't exist.
 Make sure you use the appropriate method for your use case.

Injected values are null?!?

If you are used to other DI frameworks, you may be expecting injected values to stick around.   Not always the case with Seam :

Seam will set injected fields to null when the request is finished.  Injection / uninjection happen before and after every method invocation.

So, if you access an instance outside of Seam's control, then the injected values might be null.


Tuesday, March 25, 2014

JBoss AS 7 and SLF4J

As side project at work, I'm porting a Java Enterprise 5 Seam 2 application to JBoss AS 7 (7.2, to be precise).   This application uses SLF4J for logging, and I quickly realized that without some careful configuration, the SLF4J log messages can get discarded.   That's not so great when trying to troubleshoot deployment problems!

(Side note: to post XML inside a <pre> tag on Blogger, use an HTML Encoder like this one)

Anyway, here's what I ended up doing to get it to work:
  1. Don't use the provided SLF4J module from the container.   This will allow the application to use it's own version of SLF4J and logging implementation (e.g. Log4J).   I did this by adding the following exclusions to jboss-deployment-structure.xml (like this):
            <exclusions>
                ... other modules ...
                <module name="org.apache.log4j" />
                <module name="org.slf4j" />
                <module name="org.slf4j.impl" />
            </exclusions>
    
    • Make sure to exclude the implementation org.slf4j.impl as well, otherwise the app server will supply it's own. 
    • For EAR deployments, this needs to be repeated in the sub-deployment for the WAR as well.   See this JBoss community post.

  2. Include the slf4j-api, and slf4j implementation jars (e.g. slf4j-log4j12 and log4j) in the lib directory of the EAR. In my case, this is just making sure that the Maven module for the EAR doesn't exclude these. Verify by locating the files in the target EAR.

    In the EAR pom.xml, I added the following dependencies:

           <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <scope>runtime</scope> 
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
            </dependency>
    

    In this case, the versions are specified in a dependency management pom.xml.   Also, you may need to change the scope to 'runtime' if the scope is set in the dependency management (to something else, like 'test').
  3. Put your log implementation config files where the implementation can see them.   For Lo4J, you can make a jar with log4j.xml in it, and put this in the lib directory of the ear.

Troubleshooting

Various things I encountered while setting this up...

No SLF4J Implementation


If you manage to exclude the SLF4J implementation, but the EAR doesn't contain one you may get this:

ERROR [stderr] (ServerService Thread Pool -- 65) SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
ERROR [stderr] (ServerService Thread Pool -- 65) SLF4J: Defaulting to no-operation (NOP) logger implementation
ERROR [stderr] (ServerService Thread Pool -- 65) SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

In this case, just make sure the desired SLF4J implementation class is in the EAR lib directory. For example, add it as a dependency in your pom.xml.

JBoss-Specific Logging Configuration


I had an old log4j.xml that had some references to some older JBoss-specific logging features like this:

    <category name="javax">
        <priority value="INFO" class="org.jboss.logging.log4j.JDKLevel"/>
    </category>

These references caused ClassNotFoundExeptions when Log4J was initializing. To resolve this, I simply commented out these elements.

Also, I replaced org.jboss.logging.appender.RollingFileAppender with org.apache.log4j.RollingFileAppender.
 

Sunday, January 12, 2014

Replacing a bad hard drive in a ZFS pool - Linux/zfs-fuse

Thought I'd re-post this here, for convenience. I've got a home-brew NAS server that is running Fedora, zfs-fuse, and CIFS. The situation:
  • "Disk Utility" reports that drive /dev/sde has many bad sectors.
  • zpool status shows a degraded state for the main pool.  The drive is listed in the main pool by it's id.
    # zpool status -v
      pool: nasdata
     state: DEGRADED
    status: One or more devices could not be used because the label is missing or
        invalid.  Sufficient replicas exist for the pool to continue
        functioning in a degraded state.
    action: Replace the device using 'zpool replace'.
       see: http://www.sun.com/msg/ZFS-8000-4J
     scrub: resilver completed after 0h28m with 0 errors on Sat Feb 23 23:54:32 2013
    config:
    
        NAME                                      STATE     READ WRITE CKSUM
        nasdata                                   DEGRADED     0     0     0
          raidz1-0                                DEGRADED     0     0     0
            disk/by-id/ata-ST31500541AS_5XW0PDZ1  ONLINE       0     0     0
            disk/by-id/ata-ST31500541AS_5XW0PZJQ  ONLINE       0     0     0
            disk/by-id/ata-ST31500541AS_6XW1MKZZ  UNAVAIL      0   193     6  experienced I/O failures
            disk/by-id/ata-ST31500541AS_6XW1KRR9  ONLINE       0     0     0
    
    errors: No known data errors
    
Well that pretty much sums it up. No data errors in the array itself, but the disk is unavailable. Here's the process for replacing it:

  1. Tell zfs to take the disk offline:
    # zpool offline nasdata /dev/disk/by-id/ata-ST31500541AS_6XW1MKZZ
    

    Note that I'm using /dev/disk/by-id here. This is because that is how it is listed in the pool. 
  2. Shut the machine down.
  3. Add the new disk.  I also removed the failing disk because it was causing problems during POST.
    NOTE: REMEMBER TO LABEL YOUR DISKS! This really helps when the time comes to replace them! 
  4. Start the machine up.
  5. Tell zfs about the new disk:
    # zpool replace nasdata /dev/disk/by-id/ata-ST31500541AS_6XW1MKZZ /dev/disk/by-id/ata-SAMSUNG_HD204UI_S2HGJ90BA09450
    

    Note: I had to use the disk IDs because the pool set itself up that way in the first place (I had switched the drives to a new SATA card).
  6. Immediately ZFS begins replacing the disk:
    # zpool status 
      pool: nasdata
     state: DEGRADED
    status: One or more devices is currently being resilvered.  The pool will
        continue to function, possibly in a degraded state.
    action: Wait for the resilver to complete.
     scrub: resilver in progress for 0h0m, 0.00% done, 2695h59m to go
    config:
        NAME                                                 STATE     READ WRITE CKSUM
        nasdata                                              DEGRADED     0     0     0
          raidz1-0                                           DEGRADED     0     0     0
            disk/by-id/ata-ST31500541AS_5XW0PDZ1             ONLINE       0     0     0
            disk/by-id/ata-ST31500541AS_5XW0PZJQ             ONLINE       0     0     0
            replacing-2                                      DEGRADED     0     0     0
              disk/by-id/ata-ST31500541AS_6XW1MKZZ           OFFLINE      0   193     6
              disk/by-id/ata-SAMSUNG_HD204UI_S2HGJ90BA09450  ONLINE       0     0     0  2.34M resilvered
            disk/by-id/ata-ST31500541AS_6XW1KRR9             ONLINE       0     0     0
    errors: No known data errors
    

    Now hopefully this won't take 2695 hours to complete! :) Later on the status goes down to 11h. Okay, that's doable. 
  7. Several hours later, the new drive is incorporated into the pool:
    # zpool status -v
      pool: nasdata
     state: ONLINE
     scrub: resilver completed after 9h2m with 0 errors on Sun Feb 24 10:30:21 2013
    config:
            NAME                                               STATE     READ WRITE CKSUM
            nasdata                                            ONLINE       0     0     0
              raidz1-0                                         ONLINE       0     0     0
                disk/by-id/ata-ST31500541AS_5XW0PDZ1           ONLINE       0     0     0
                disk/by-id/ata-ST31500541AS_5XW0PZJQ           ONLINE       0     0     0
                disk/by-id/ata-SAMSUNG_HD204UI_S2HGJ90BA09450  ONLINE       0     0     0  916G resilvered
                disk/by-id/ata-ST31500541AS_6XW1KRR9           ONLINE       0     0     0
    errors: No known data errors
So that's it. While it was re-slivering, the ZFS filesystem was completely available. How nice! My SMB/CIFS shares were working just fine.