Tuesday, January 26, 2016

Find circular dependencies in Spring Autowired services before Spring does

We use Spring annotation based configuration, and @Autowire the dependencies. Using Autowire injection is a more convenient way than using constructor based injection, with less code to write. However, if your code grows, you may end up with circular dependencies in your code. While spring does a great job of still instantiating beans that have a circular reference, after too many circular references, you will end up getting an exception like this:

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'agencyBean': Requested bean is currently in creation: Is there an unresolvable circular reference?
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:252)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:844)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:786)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:703)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:474)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:282)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$2.getObject(AbstractBeanFactory.java:329)
    at com.myapp.utils.ViewScope.get(ViewScope.java:20)
    public class CircularDependencyUtil {
    private static final Logger logger = LoggerFactory.getLogger(CircularDependencyUtil.class);

When you get this exception, you might have reached a point where you have 50 services that have been autowired in such a way that you have ended up with multiple spring bean circular references. At this moment it is best to find out all the beans that have a circular reference.

Here is what you need to do:

First, add maven dependencies to jgrapht to your pom.xml
        
            org.jgrapht
            jgrapht-core
            0.9.1
        
        
            org.jgrapht
            jgrapht-ext
            0.9.0
        
Second, add the following code to a class of your choice.
    public static DirectedGraph generateDependencyGraphForServices(String packageName) {
        DirectedGraph dgraph = new DefaultDirectedGraph(DefaultEdge.class);
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(true);

        scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

        for (BeanDefinition bd : scanner.findCandidateComponents(packageName)) {
            try {
                Class beanClass = Class.forName(((ScannedGenericBeanDefinition) bd).getMetadata().getClassName());

                if (beanClass.getInterfaces().length == 0)
                    continue;
                String beanName = beanClass.getInterfaces()[0].getSimpleName();
                if (!beanClass.getInterfaces()[0].getName().startsWith(packageName)) { // only common services
                    continue;
                }
                if (!dgraph.containsVertex(beanName)) {
                    dgraph.addVertex(beanName);
                }
                Field[] fields = beanClass.getDeclaredFields();
                if (fields.length == 0)
                    continue;
                for (Field field : fields) {
                    if (!field.isAnnotationPresent(Autowired.class)) {
                        continue;
                    }
                    if (!field.getType().getName().startsWith(packageName)) { // only common services
                        continue;
                    }
                    String fieldName = field.getType().getSimpleName();
                    if (!dgraph.containsVertex(fieldName)) {
                        dgraph.addVertex(fieldName);
                    }
                    dgraph.addEdge(beanName, fieldName);
                    logger.info("Field={}", fieldName);
                }
            } catch (ClassNotFoundException e) {
                continue;
            }
        }
        return dgraph;
    }

    public static Set getCycleInDependencyGraph(DirectedGraph dgraph){
        CycleDetector cycleDetector = new CycleDetector<>(dgraph);
        Set cycles = cycleDetector.findCycles();
        return cycles;
    }

    public static OutputStream exportGraphInDOTFormat(DirectedGraph dgraph){
        VertexNameProvider vertexNameProvider = new VertexNameProvider() {
            @Override
            public String getVertexName(String vertex) {
                return vertex;
            }
        };
        DOTExporter exporter = new DOTExporter(vertexNameProvider, null, null, null, null);
        OutputStream outputStream = new ByteArrayOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        try {
            exporter.export(outputStreamWriter, dgraph);
            //exporter.export(new FileWriter(targetDirectory + "initial-graph.dot"), dgraph);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return outputStream;
    }
}

The function generateDependencyGraphForServices(String packageName) will create a graph of all the Beans in the package that have the Service annotation and their Autowired dependencies. After the graph is created, pass it to the getCycleInDependencyGraph(DirectedGraph dgraph) function, which will find all the beans that have a circular dependency.

Circular dependencies are never good, so even if Spring does work around them most of the times, it is best to not have them in your code.


Thursday, April 25, 2013

Require.js and QUnit Integration

When I was integrating Require.js and QUnit I googled around to find what people were doing. Turns out many people were using something like this.
While this works well, it requires you to add reference to the QUnit javascript directly through the script tag, and not through the regular define function. This goes against the AMD principles.

Here is what I've come up with:

First, add the QUnit file to the require.js config, like this:

    requirejs.config({
        paths: {
            'jquery': '//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery',
            'qunit-1.11.0':
        },
        shim: {
            'qunit-1.11.0': ['jquery']
        }
    });

Second, in your core test file add this

define(['jquery', 'qunit-1.11.0'], function ($) {

    // load the qunit css
    $('head').append($('').attr('href', '//' + devCenterConfig.staticBaseUrl + '/css/qunit-1.11.0.css'));
    $("body").append('
');

    // initialize qunit
    QUnit.config.autostart = false;
    QUnit.load();
.....

Last, After your tests have been defined, call this function:
QUnit.start();

Hope this helps.



Friday, July 1, 2011

A better way to add custom buttons to fancybox

So a few people contacted me that the earlier method doesn't work with the new fancybox. The method was a hack into fancybox anyways, and I've (along with our html/css designer and other developers) have a better way to do this. Here it is:

function formatTheTitle(title) {
return ''<div id="fancy-title"><div id="newWindow" class="fancyPopupDivNewWindow" onclick="newWindow()">&nbsp;</div>' + title + '</div>';
}

$("#theDiv").fancybox({
'centerOnScroll': true,
'showCloseButton': true,
'titlePosition': 'inside',
'padding': 0,
'scrolling': 'yes',
'showNavArrows': false,
'titleFormat': formatTheTitle
'title': '',
'type': 'iframe',
});

Basically you can ride on the format title property to add another button to the fancybox. This is the css you require

/* open in new window icon */
.fancyPopupDivNewWindow {
position: absolute;
height: 30px;
width: 30px;
cursor: pointer;
z-index: 800;
display: inline-block;
top: -15px;
right: 18px;
background: transparent url(../images/fancybox/newwindow.png) 0 0 no-repeat;
}

Monday, July 26, 2010

Another reason for the Flex #2032 error

So I came across another reason for the Flex #2032 error. In my case, it wasn't the crossdomain.xml that was the problem. This was an IE7/8 specific issue, Firefox was working fine. After a lot of head banging I found out this about Flex:
Flex strips the request of headers if you make a GET request. So if for some reason the server requires headers (like in my case), you will get a #2032 error. Just change it to a POST request if you have headers to send.

Wednesday, May 5, 2010

Adding custom buttons to jQuery's fancybox


The fancybox is a really cool way of displaying images, divs, etc. I really love its simplicity and coolness. I have started using it in many places now. But I had a problem. I wanted to add buttons next to the close button. Just like Windows has the maximize and minimize buttons next to the close button, I wanted a button that would open in a new window the contents of the fancybox.

I tried to figure out ways to do it without changing the code or the css, but it proved difficult. I would have had to use the onStart function and make changes to the outer div of fancybox dynamically. And also have to add functions for onClick, etc. So I decided that the best way would be to customize the fancybox.

So here is what needs to be done:

Add this to jquery.fancybox-1.3.1.css
#fancybox-customButton1 {
position: absolute;
top: -15px;
right: 15px;
width: 30px;
height: 30px;
background-position: 0px 0px;
cursor: pointer;
z-index: 1103;
display: none;
}


Add all of the following red lines to jquery-fancybox.1.3.1.js

Add var customButton1 on line 19
(function($) {
var tmp, loading, overlay, wrap, outer, inner, close, nav_left, nav_right, customButton1,


Add function on line 173
$('#fancybox-title').appendTo( outer ).hide();
},

fancybox_process_customButton1 = function() {
$('#fancybox-customButton1').css("background-image", "url(" + currentOpts.customButton1Image + ")");
}


Add this to _finish = function () around line 268
if (currentOpts.showCloseButton) {
close.show();
}
if (currentOpts.showCustomButton1) {
customButton1.show();
}

Add this around line 400,
fancybox_process_title();

fancybox_process_customButton1();

Around line 603, add the defaults
selectedOpts.showCloseButton = false;
selectedOpts.showCustomButton1 = false;

Around line 785, add the div
outer.append(
inner = $('
'),

close = $(''),
customButton1 = $(''),

nav_left = $(''),
nav_right = $('')
);

close.click($.fancybox.close);
customButton1.click($.fancybox.onCustomButton1Click);



Around line 995
} else {
wrap.fadeOut( currentOpts.transitionOut == 'none' ? 0 : currentOpts.speedOut, _cleanup);
}
};

$.fancybox.onCustomButton1Click = function() {
currentOpts.onCustomButton1Click();



Around line 1086, add the options that the user can change
showCloseButton : true,
showNavArrows : true,
enableEscapeButton : true,

showCustomButton1 : false,
customButton1Image : null,
customButton1Title : null,
onCustomButton1Click: null,

Friday, December 11, 2009

New laptop screens have lower resolutions

Anyone bought a new laptop recently? Anyone?

My four year old laptop is a 1GB, 2.0 GHz pentium. It has a 15" screen, with a maximum resolution of 1440 x 1024. It works beautifully, but after 4 years of manhandling, it has started to show its age. So began the search for a new laptop.

Technology for laptop screens has changed over the years, but I always expected it to get better and cheaper. Surprisingly, it hasn't. All the major laptop companies (Dell, Lenovo, HP) only have widescreen laptops now. They are advertised as 'HD' screens, and the advertised resolution is 720p and 1080p rather than the usual. The worst part is, a 15.6" 720p laptop screen has a maximum screen resolution of 1336 x 768, a lot worse than my 4 year old laptop!

Since I use my laptop for surfing the web and coding, a widescreen is useless to me. My wife and I hardly watch movies on the laptop, so what do we do with the right 25% the screen, which remains barren all the time?

Curious as to why laptop companies would simply stop caring about how people use their laptop, I searched the web for answers. Seems like now the screens come directly from TV manufacturers, who use the same technology to cut laptop screens that they use for your HD TV. This technology seems to be a lot cheaper than that used for the old screens, but it offers such a low resolution that I wonder if we are back in the 90's?

Wednesday, December 9, 2009

.reg file created by regasm cannot be ported between 32-bit and 64-bit!

When you create a .reg file using regasm

While working on the WS-Security project, I found out this really weird behavior. I was building my .NET dll on Win XP 64-bit, IIS6, VS2005 machine. But the .NET dll itself was compiled in 32-bit. I used Regasm to create a .reg file, which I then double-clicked to add registry entries.

But it wouldn't work! For some reason the registry entries could not be found! This worked fine on a 32-bit Win XP system. At last, I found out the reason: http://support.microsoft.com/kb/896459

The solution: Don't create a .reg file. Just use regasm .dll. Creating a .reg file for a 32-bit program doesn't add it correctly to a 64-bit system.