JCLOUDS-253: Properly parse Chef Server version
diff --git a/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java b/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java
index f7457fe..67dd35a 100644
--- a/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java
+++ b/core/src/main/java/org/jclouds/chef/config/ChefParserModule.java
@@ -31,8 +31,6 @@
 import java.security.spec.InvalidKeySpecException;
 import java.util.Map;
 import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -42,6 +40,7 @@
 import org.jclouds.chef.functions.ParseCookbookVersionsV09FromJson;
 import org.jclouds.chef.functions.ParseCookbookVersionsV10FromJson;
 import org.jclouds.chef.functions.ParseKeySetFromJson;
+import org.jclouds.chef.suppliers.ChefVersionSupplier;
 import org.jclouds.crypto.Crypto;
 import org.jclouds.crypto.Pems;
 import org.jclouds.http.HttpResponse;
@@ -49,7 +48,6 @@
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
 import org.jclouds.json.internal.NullFilteringTypeAdapterFactories.MapTypeAdapterFactory;
 import org.jclouds.json.internal.NullHackJsonLiteralAdapter;
-import org.jclouds.rest.annotations.ApiVersion;
 
 import com.google.common.base.Charsets;
 import com.google.common.base.Function;
@@ -307,31 +305,17 @@
    @Provides
    @Singleton
    @CookbookParser
-   public Function<HttpResponse, Set<String>> provideCookbookDefinitionAdapter(@ApiVersion String apiVersion,
+   public Function<HttpResponse, Set<String>> provideCookbookDefinitionAdapter(ChefVersionSupplier chefVersionSupplier,
          ParseCookbookDefinitionFromJson v10parser, ParseKeySetFromJson v09parser) {
-      Pattern versionPattern = Pattern.compile("\\d\\.(\\d)\\.\\d");
-      Matcher m = versionPattern.matcher(apiVersion);
-      if (m.matches()) {
-         return Integer.valueOf(m.group(1)) > 9 ? v10parser : v09parser;
-      } else {
-         // Default to the latest version of the parser
-         return v10parser;
-      }
+      return chefVersionSupplier.get() >= 10 ? v10parser : v09parser;
    }
 
    @Provides
    @Singleton
    @CookbookVersionsParser
-   public Function<HttpResponse, Set<String>> provideCookbookDefinitionAdapter(@ApiVersion String apiVersion,
+   public Function<HttpResponse, Set<String>> provideCookbookDefinitionAdapter(ChefVersionSupplier chefVersionSupplier,
          ParseCookbookVersionsV10FromJson v10parser, ParseCookbookVersionsV09FromJson v09parser) {
-      Pattern versionPattern = Pattern.compile("\\d\\.(\\d)\\.\\d");
-      Matcher m = versionPattern.matcher(apiVersion);
-      if (m.matches()) {
-         return Integer.valueOf(m.group(1)) > 9 ? v10parser : v09parser;
-      } else {
-         // Default to the latest version of the parser
-         return v10parser;
-      }
+      return chefVersionSupplier.get() >= 10 ? v10parser : v09parser;
    }
 
    @Override
diff --git a/core/src/main/java/org/jclouds/chef/suppliers/ChefVersionSupplier.java b/core/src/main/java/org/jclouds/chef/suppliers/ChefVersionSupplier.java
new file mode 100644
index 0000000..a08fc40
--- /dev/null
+++ b/core/src/main/java/org/jclouds/chef/suppliers/ChefVersionSupplier.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.suppliers;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.jclouds.chef.config.ChefProperties;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.annotations.ApiVersion;
+
+import com.google.common.base.Supplier;
+
+/**
+ * Properly supply the version of the Chef Server.
+ * 
+ * @author Ignasi Barrera
+ */
+@Singleton
+public class ChefVersionSupplier implements Supplier<Integer> {
+
+   /** The default version to assume in case we can not parse it. */
+   public static final Integer DEFAULT_VERSION = 10;
+
+   @Resource
+   @Named(ChefProperties.CHEF_LOGGER)
+   private Logger logger = Logger.NULL;
+
+   /** The configured version of the Chef Server API. */
+   private final String apiVersion;
+
+   @Inject
+   ChefVersionSupplier(@ApiVersion String apiVersion) {
+      this.apiVersion = checkNotNull(apiVersion, "apiVersion must not be null");
+   }
+
+   @Override
+   public Integer get() {
+      // Old versions of Chef have versions like 0.9.x, 0.10.x, but newer
+      // versions are in the format 10.x.y, 11.x.y
+      Pattern versionPattern = Pattern.compile("(?:0\\.(\\d+)|(\\d+)\\.\\d+)(?:\\.\\d)*");
+
+      Matcher m = versionPattern.matcher(apiVersion);
+      if (!m.matches()) {
+         logger.warn("Configured version does not match the standard version pattern. Assuming version %s",
+               DEFAULT_VERSION);
+         return DEFAULT_VERSION;
+      }
+
+      return Integer.valueOf(firstNonNull(m.group(1), m.group(2)));
+   }
+
+}
diff --git a/core/src/test/java/org/jclouds/chef/suppliers/ChefVersionSupplierTest.java b/core/src/test/java/org/jclouds/chef/suppliers/ChefVersionSupplierTest.java
new file mode 100644
index 0000000..7bde5ac
--- /dev/null
+++ b/core/src/test/java/org/jclouds/chef/suppliers/ChefVersionSupplierTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.chef.suppliers;
+
+import static org.jclouds.chef.suppliers.ChefVersionSupplier.DEFAULT_VERSION;
+import static org.testng.Assert.assertEquals;
+
+import org.testng.annotations.Test;
+
+/**
+ * Unit tests for the {@link ChefVersionSupplier} class.
+ * 
+ * @author Ignasi Barrera
+ */
+@Test(groups = "unit", testName = "ChefVersionSupplierTest")
+public class ChefVersionSupplierTest {
+
+   public void testReturnsDefaultVersion() {
+      assertEquals(new ChefVersionSupplier("15").get(), DEFAULT_VERSION);
+      assertEquals(new ChefVersionSupplier("0").get(), DEFAULT_VERSION);
+      assertEquals(new ChefVersionSupplier("0.").get(), DEFAULT_VERSION);
+   }
+
+   public void testReturnsMajorVersionIfNotZero() {
+      assertEquals(new ChefVersionSupplier("11.6").get().intValue(), 11);
+      assertEquals(new ChefVersionSupplier("11.6.0").get().intValue(), 11);
+      assertEquals(new ChefVersionSupplier("11.6.0.1").get().intValue(), 11);
+   }
+
+   public void testReturnsMinorVersionIfMajorIsZero() {
+      assertEquals(new ChefVersionSupplier("0.9").get().intValue(), 9);
+      assertEquals(new ChefVersionSupplier("0.9.8").get().intValue(), 9);
+      assertEquals(new ChefVersionSupplier("0.9.8.2").get().intValue(), 9);
+   }
+}