http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathTrie.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathTrie.java b/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathTrie.java new file mode 100644 index 0000000..73053e0 --- /dev/null +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathTrie.java @@ -0,0 +1,293 @@ + /** + * 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.apache.zookeeper.common; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * a class that implements prefix matching for + * components of a filesystem path. the trie + * looks like a tree with edges mapping to + * the component of a path. + * example /ab/bc/cf would map to a trie + * / + * ab/ + * (ab) + * bc/ + * / + * (bc) + * cf/ + * (cf) + */ +public class PathTrie { + /** + * the logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(PathTrie.class); + + /** + * the root node of PathTrie + */ + private final TrieNode rootNode ; + + static class TrieNode { + boolean property = false; + final HashMap<String, TrieNode> children; + TrieNode parent = null; + /** + * create a trienode with parent + * as parameter + * @param parent the parent of this trienode + */ + private TrieNode(TrieNode parent) { + children = new HashMap<String, TrieNode>(); + this.parent = parent; + } + + /** + * get the parent of this node + * @return the parent node + */ + TrieNode getParent() { + return this.parent; + } + + /** + * set the parent of this node + * @param parent the parent to set to + */ + void setParent(TrieNode parent) { + this.parent = parent; + } + + /** + * a property that is set + * for a node - making it + * special. + */ + void setProperty(boolean prop) { + this.property = prop; + } + + /** the property of this + * node + * @return the property for this + * node + */ + boolean getProperty() { + return this.property; + } + /** + * add a child to the existing node + * @param childName the string name of the child + * @param node the node that is the child + */ + void addChild(String childName, TrieNode node) { + synchronized(children) { + if (children.containsKey(childName)) { + return; + } + children.put(childName, node); + } + } + + /** + * delete child from this node + * @param childName the string name of the child to + * be deleted + */ + void deleteChild(String childName) { + synchronized(children) { + if (!children.containsKey(childName)) { + return; + } + TrieNode childNode = children.get(childName); + // this is the only child node. + if (childNode.getChildren().length == 1) { + childNode.setParent(null); + children.remove(childName); + } + else { + // their are more child nodes + // so just reset property. + childNode.setProperty(false); + } + } + } + + /** + * return the child of a node mapping + * to the input childname + * @param childName the name of the child + * @return the child of a node + */ + TrieNode getChild(String childName) { + synchronized(children) { + if (!children.containsKey(childName)) { + return null; + } + else { + return children.get(childName); + } + } + } + + /** + * get the list of children of this + * trienode. + * @param node to get its children + * @return the string list of its children + */ + String[] getChildren() { + synchronized(children) { + return children.keySet().toArray(new String[0]); + } + } + + /** + * get the string representation + * for this node + */ + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Children of trienode: "); + synchronized(children) { + for (String str: children.keySet()) { + sb.append(" " + str); + } + } + return sb.toString(); + } + } + + /** + * construct a new PathTrie with + * a root node of / + */ + public PathTrie() { + this.rootNode = new TrieNode(null); + } + + /** + * add a path to the path trie + * @param path + */ + public void addPath(String path) { + if (path == null) { + return; + } + String[] pathComponents = path.split("/"); + TrieNode parent = rootNode; + String part = null; + if (pathComponents.length <= 1) { + throw new IllegalArgumentException("Invalid path " + path); + } + for (int i=1; i<pathComponents.length; i++) { + part = pathComponents[i]; + if (parent.getChild(part) == null) { + parent.addChild(part, new TrieNode(parent)); + } + parent = parent.getChild(part); + } + parent.setProperty(true); + } + + /** + * delete a path from the trie + * @param path the path to be deleted + */ + public void deletePath(String path) { + if (path == null) { + return; + } + String[] pathComponents = path.split("/"); + TrieNode parent = rootNode; + String part = null; + if (pathComponents.length <= 1) { + throw new IllegalArgumentException("Invalid path " + path); + } + for (int i=1; i<pathComponents.length; i++) { + part = pathComponents[i]; + if (parent.getChild(part) == null) { + //the path does not exist + return; + } + parent = parent.getChild(part); + LOG.info("{}",parent); + } + TrieNode realParent = parent.getParent(); + realParent.deleteChild(part); + } + + /** + * return the largest prefix for the input path. + * @param path the input path + * @return the largest prefix for the input path. + */ + public String findMaxPrefix(String path) { + if (path == null) { + return null; + } + if ("/".equals(path)) { + return path; + } + String[] pathComponents = path.split("/"); + TrieNode parent = rootNode; + List<String> components = new ArrayList<String>(); + if (pathComponents.length <= 1) { + throw new IllegalArgumentException("Invalid path " + path); + } + int i = 1; + String part = null; + StringBuilder sb = new StringBuilder(); + int lastindex = -1; + while((i < pathComponents.length)) { + if (parent.getChild(pathComponents[i]) != null) { + part = pathComponents[i]; + parent = parent.getChild(part); + components.add(part); + if (parent.getProperty()) { + lastindex = i-1; + } + } + else { + break; + } + i++; + } + for (int j=0; j< (lastindex+1); j++) { + sb.append("/" + components.get(j)); + } + return sb.toString(); + } + + /** + * clear all nodes + */ + public void clear() { + for(String child : rootNode.getChildren()) { + rootNode.deleteChild(child); + } + } +}
http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathUtils.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathUtils.java new file mode 100644 index 0000000..2a6c7ef --- /dev/null +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/PathUtils.java @@ -0,0 +1,103 @@ + /** + * 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.apache.zookeeper.common; + + +/** + * Path related utilities + */ +public class PathUtils { + + /** validate the provided znode path string + * @param path znode path string + * @param isSequential if the path is being created + * with a sequential flag + * @throws IllegalArgumentException if the path is invalid + */ + public static void validatePath(String path, boolean isSequential) + throws IllegalArgumentException { + validatePath(isSequential? path + "1": path); + } + + /** + * Validate the provided znode path string + * @param path znode path string + * @throws IllegalArgumentException if the path is invalid + */ + public static void validatePath(String path) throws IllegalArgumentException { + if (path == null) { + throw new IllegalArgumentException("Path cannot be null"); + } + if (path.length() == 0) { + throw new IllegalArgumentException("Path length must be > 0"); + } + if (path.charAt(0) != '/') { + throw new IllegalArgumentException( + "Path must start with / character"); + } + if (path.length() == 1) { // done checking - it's the root + return; + } + if (path.charAt(path.length() - 1) == '/') { + throw new IllegalArgumentException( + "Path must not end with / character"); + } + + String reason = null; + char lastc = '/'; + char chars[] = path.toCharArray(); + char c; + for (int i = 1; i < chars.length; lastc = chars[i], i++) { + c = chars[i]; + + if (c == 0) { + reason = "null character not allowed @" + i; + break; + } else if (c == '/' && lastc == '/') { + reason = "empty node name specified @" + i; + break; + } else if (c == '.' && lastc == '.') { + if (chars[i-2] == '/' && + ((i + 1 == chars.length) + || chars[i+1] == '/')) { + reason = "relative paths not allowed @" + i; + break; + } + } else if (c == '.') { + if (chars[i-1] == '/' && + ((i + 1 == chars.length) + || chars[i+1] == '/')) { + reason = "relative paths not allowed @" + i; + break; + } + } else if (c > '\u0000' && c < '\u001f' + || c > '\u007f' && c < '\u009F' + || c > '\ud800' && c < '\uf8ff' + || c > '\ufff0' && c < '\uffff') { + reason = "invalid character @" + i; + break; + } + } + + if (reason != null) { + throw new IllegalArgumentException( + "Invalid path string \"" + path + "\" caused by " + reason); + } + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/common/Time.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/common/Time.java b/zookeeper-server/src/main/java/org/apache/zookeeper/common/Time.java new file mode 100644 index 0000000..83e53f0 --- /dev/null +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/common/Time.java @@ -0,0 +1,52 @@ +/** + * 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.apache.zookeeper.common; + +import java.util.Date; + +public class Time { + /** + * Returns time in milliseconds as does System.currentTimeMillis(), + * but uses elapsed time from an arbitrary epoch more like System.nanoTime(). + * The difference is that if somebody changes the system clock, + * Time.currentElapsedTime will change but nanoTime won't. On the other hand, + * all of ZK assumes that time is measured in milliseconds. + * @return The time in milliseconds from some arbitrary point in time. + */ + public static long currentElapsedTime() { + return System.nanoTime() / 1000000; + } + + /** + * Explicitly returns system dependent current wall time. + * @return Current time in msec. + */ + public static long currentWallTime() { + return System.currentTimeMillis(); + } + + /** + * This is to convert the elapsedTime to a Date. + * @return A date object indicated by the elapsedTime. + */ + public static Date elapsedTimeToDate(long elapsedTime) { + long wallTime = currentWallTime() + elapsedTime - currentElapsedTime(); + return new Date(wallTime); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java new file mode 100644 index 0000000..67484e4 --- /dev/null +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java @@ -0,0 +1,298 @@ +/** + * 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.apache.zookeeper.util; + +import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.zookeeper.SaslClientCallbackHandler; +import org.apache.zookeeper.server.auth.KerberosName; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; +import org.slf4j.Logger; + +public final class SecurityUtils { + + public static final String QUORUM_HOSTNAME_PATTERN = "_HOST"; + + /** + * Create an instance of a SaslClient. It will return null if there is an exception. + * + * @param subject subject + * @param servicePrincipal principal + * @param protocol name of the protocol for which the authentication is being performed + * @param serverName name of the server to authenticate to + * @param LOG logger + * @param entity can be either zookeeper client or quorum learner + * + * @return saslclient object + * @throws SaslException + */ + public static SaslClient createSaslClient(final Subject subject, + final String servicePrincipal, final String protocol, + final String serverName, final Logger LOG, final String entity) throws SaslException { + SaslClient saslClient; + // Use subject.getPrincipals().isEmpty() as an indication of which SASL + // mechanism to use: if empty, use DIGEST-MD5; otherwise, use GSSAPI. + if (subject.getPrincipals().isEmpty()) { + // no principals: must not be GSSAPI: use DIGEST-MD5 mechanism + // instead. + LOG.info("{} will use DIGEST-MD5 as SASL mechanism.", entity); + String[] mechs = { "DIGEST-MD5" }; + String username = (String) (subject.getPublicCredentials() + .toArray()[0]); + String password = (String) (subject.getPrivateCredentials() + .toArray()[0]); + // 'domain' parameter is hard-wired between the server and client + saslClient = Sasl.createSaslClient(mechs, username, protocol, + serverName, null, new SaslClientCallbackHandler(password, entity)); + return saslClient; + } else { // GSSAPI. + final Object[] principals = subject.getPrincipals().toArray(); + // determine client principal from subject. + final Principal clientPrincipal = (Principal) principals[0]; + boolean usingNativeJgss = Boolean + .getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a particular + // Subject, e.g. Subject.doAs(...) or + // Subject.doAsPrivileged(...), + // the to-be-used GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations will + // fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSCredential cred = manager.createCredential(null, + GSSContext.DEFAULT_LIFETIME, krb5Mechanism, + GSSCredential.INITIATE_ONLY); + subject.getPrivateCredentials().add(cred); + LOG.debug("Added private credential to {} principal name: '{}'", + entity, clientPrincipal); + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "authentication at the server may fail", ex); + } + } + final KerberosName clientKerberosName = new KerberosName( + clientPrincipal.getName()); + // assume that server and client are in the same realm (by default; + // unless the system property + // "zookeeper.server.realm" is set). + String serverRealm = System.getProperty("zookeeper.server.realm", + clientKerberosName.getRealm()); + KerberosName serviceKerberosName = new KerberosName( + servicePrincipal + "@" + serverRealm); + final String serviceName = serviceKerberosName.getServiceName(); + final String serviceHostname = serviceKerberosName.getHostName(); + final String clientPrincipalName = clientKerberosName.toString(); + try { + saslClient = Subject.doAs(subject, + new PrivilegedExceptionAction<SaslClient>() { + public SaslClient run() throws SaslException { + LOG.info("{} will use GSSAPI as SASL mechanism.", entity); + String[] mechs = { "GSSAPI" }; + LOG.debug("creating sasl client: {}={};service={};serviceHostname={}", + new Object[] { entity, clientPrincipalName, serviceName, serviceHostname }); + SaslClient saslClient = Sasl.createSaslClient( + mechs, clientPrincipalName, serviceName, + serviceHostname, null, + new SaslClientCallbackHandler(null, entity)); + return saslClient; + } + }); + return saslClient; + } catch (Exception e) { + LOG.error("Exception while trying to create SASL client", e); + return null; + } + } + } + + /** + * Create an instance of a SaslServer. It will return null if there is an exception. + * + * @param subject subject + * @param protocol protocol + * @param serverName server name + * @param callbackHandler login callback handler + * @param LOG logger + * @return sasl server object + */ + public static SaslServer createSaslServer(final Subject subject, + final String protocol, final String serverName, + final CallbackHandler callbackHandler, final Logger LOG) { + if (subject != null) { + // server is using a JAAS-authenticated subject: determine service + // principal name and hostname from zk server's subject. + if (subject.getPrincipals().size() > 0) { + try { + final Object[] principals = subject.getPrincipals() + .toArray(); + final Principal servicePrincipal = (Principal) principals[0]; + + // e.g. servicePrincipalNameAndHostname := + // "zookeeper/myhost.foo....@foo.com" + final String servicePrincipalNameAndHostname = servicePrincipal + .getName(); + + int indexOf = servicePrincipalNameAndHostname.indexOf("/"); + + // e.g. servicePrincipalName := "zookeeper" + final String servicePrincipalName = servicePrincipalNameAndHostname + .substring(0, indexOf); + + // e.g. serviceHostnameAndKerbDomain := + // "myhost.foo....@foo.com" + final String serviceHostnameAndKerbDomain = servicePrincipalNameAndHostname + .substring(indexOf + 1, + servicePrincipalNameAndHostname.length()); + + indexOf = serviceHostnameAndKerbDomain.indexOf("@"); + // e.g. serviceHostname := "myhost.foo.com" + final String serviceHostname = serviceHostnameAndKerbDomain + .substring(0, indexOf); + + // TODO: should depend on zoo.cfg specified mechs, but if + // subject is non-null, it can be assumed to be GSSAPI. + final String mech = "GSSAPI"; + + LOG.debug("serviceHostname is '" + serviceHostname + "'"); + LOG.debug("servicePrincipalName is '" + servicePrincipalName + + "'"); + LOG.debug("SASL mechanism(mech) is '" + mech + "'"); + + boolean usingNativeJgss = Boolean + .getBoolean("sun.security.jgss.native"); + if (usingNativeJgss) { + // http://docs.oracle.com/javase/6/docs/technotes/guides/security/jgss/jgss-features.html + // """ + // In addition, when performing operations as a + // particular + // Subject, e.g. Subject.doAs(...) or + // Subject.doAsPrivileged(...), the to-be-used + // GSSCredential should be added to Subject's + // private credential set. Otherwise, the GSS operations + // will fail since no credential is found. + // """ + try { + GSSManager manager = GSSManager.getInstance(); + Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2"); + GSSName gssName = manager.createName( + servicePrincipalName + "@" + + serviceHostname, + GSSName.NT_HOSTBASED_SERVICE); + GSSCredential cred = manager.createCredential( + gssName, GSSContext.DEFAULT_LIFETIME, + krb5Mechanism, GSSCredential.ACCEPT_ONLY); + subject.getPrivateCredentials().add(cred); + LOG.debug("Added private credential to service principal name: '{}'," + + " GSSCredential name: {}", servicePrincipalName, cred.getName()); + } catch (GSSException ex) { + LOG.warn("Cannot add private credential to subject; " + + "clients authentication may fail", ex); + } + } + try { + return Subject.doAs(subject, + new PrivilegedExceptionAction<SaslServer>() { + public SaslServer run() { + try { + SaslServer saslServer; + saslServer = Sasl.createSaslServer( + mech, servicePrincipalName, + serviceHostname, null, + callbackHandler); + return saslServer; + } catch (SaslException e) { + LOG.error("Zookeeper Server failed to create a SaslServer to interact with a client during session initiation: ", e); + return null; + } + } + }); + } catch (PrivilegedActionException e) { + // TODO: exit server at this point(?) + LOG.error("Zookeeper Quorum member experienced a PrivilegedActionException exception while creating a SaslServer using a JAAS principal context:", e); + } + } catch (IndexOutOfBoundsException e) { + LOG.error("server principal name/hostname determination error: ", e); + } + } else { + // JAAS non-GSSAPI authentication: assuming and supporting only + // DIGEST-MD5 mechanism for now. + // TODO: use 'authMech=' value in zoo.cfg. + try { + SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5", + protocol, serverName, null, callbackHandler); + return saslServer; + } catch (SaslException e) { + LOG.error("Zookeeper Quorum member failed to create a SaslServer to interact with a client during session initiation", e); + } + } + } + return null; + } + + /** + * Convert Kerberos principal name pattern to valid Kerberos principal name. + * If the principal name contains hostname pattern "_HOST" then it replaces + * with the given hostname, which should be fully-qualified domain name. + * + * @param principalConfig + * the Kerberos principal name conf value to convert + * @param hostname + * the fully-qualified domain name used for substitution + * @return converted Kerberos principal name + */ + public static String getServerPrincipal(String principalConfig, + String hostname) { + String[] components = getComponents(principalConfig); + if (components == null || components.length != 2 + || !components[1].equals(QUORUM_HOSTNAME_PATTERN)) { + return principalConfig; + } else { + return replacePattern(components, hostname); + } + } + + private static String[] getComponents(String principalConfig) { + if (principalConfig == null) + return null; + return principalConfig.split("[/]"); + } + + private static String replacePattern(String[] components, String hostname) { + return components[0] + "/" + hostname.toLowerCase(); + } +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java new file mode 100644 index 0000000..7285a2b --- /dev/null +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/version/util/VerGen.java @@ -0,0 +1,178 @@ +/** + * 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.apache.zookeeper.version.util; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class VerGen { + private static final String PACKAGE_NAME = "org.apache.zookeeper.version"; + private static final String TYPE_NAME = "Info"; + + static void printUsage() { + System.out.print("Usage:\tjava -cp <classpath> org.apache.zookeeper." + + "version.util.VerGen maj.min.micro[-qualifier] rev buildDate"); + System.exit(1); + } + + public static void generateFile(File outputDir, Version version, String rev, String buildDate) throws IOException + { + String path = PACKAGE_NAME.replaceAll("\\.", "/"); + File pkgdir = new File(outputDir, path); + if (!pkgdir.exists()) { + // create the pkg directory + boolean ret = pkgdir.mkdirs(); + if (!ret) { + System.out.println("Cannnot create directory: " + path); + System.exit(1); + } + } else if (!pkgdir.isDirectory()) { + // not a directory + System.out.println(path + " is not a directory."); + System.exit(1); + } + File file = new File(pkgdir, TYPE_NAME + ".java"); + FileWriter w = null; + try { + w = new FileWriter(file); + w.write("// Do not edit!\n// File generated by org.apache.zookeeper" + + ".version.util.VerGen.\n"); + w.write("/**\n"); + w.write("* Licensed to the Apache Software Foundation (ASF) under one\n"); + w.write("* or more contributor license agreements. See the NOTICE file\n"); + w.write("* distributed with this work for additional information\n"); + w.write("* regarding copyright ownership. The ASF licenses this file\n"); + w.write("* to you under the Apache License, Version 2.0 (the\n"); + w.write("* \"License\"); you may not use this file except in compliance\n"); + w.write("* with the License. You may obtain a copy of the License at\n"); + w.write("*\n"); + w.write("* http://www.apache.org/licenses/LICENSE-2.0\n"); + w.write("*\n"); + w.write("* Unless required by applicable law or agreed to in writing, software\n"); + w.write("* distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + w.write("* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + w.write("* See the License for the specific language governing permissions and\n"); + w.write("* limitations under the License.\n"); + w.write("*/\n"); + w.write("\n"); + w.write("package " + PACKAGE_NAME + ";\n\n"); + w.write("public interface " + TYPE_NAME + " {\n"); + w.write(" int MAJOR=" + version.maj + ";\n"); + w.write(" int MINOR=" + version.min + ";\n"); + w.write(" int MICRO=" + version.micro + ";\n"); + w.write(" String QUALIFIER=" + + (version.qualifier == null ? null : + "\"" + version.qualifier + "\"") + + ";\n"); + if (rev.equals("-1")) { + System.out.println("Unknown REVISION number, using " + rev); + } + w.write(" int REVISION=-1; //TODO: remove as related to SVN VCS\n"); + w.write(" String REVISION_HASH=\"" + rev + "\";\n"); + w.write(" String BUILD_DATE=\"" + buildDate + + "\";\n"); + w.write("}\n"); + } finally { + if (w != null) { + try { + w.close(); + } catch (IOException e) { + System.out.println("Unable to close file writer" + + e.getMessage()); + } + } + } + } + + public static class Version { + public int maj; + public int min; + public int micro; + public String qualifier; + } + + public static Version parseVersionString(String input) { + Version result = new Version(); + + Pattern p = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)((\\.\\d+)*)(-(.+))?$"); + Matcher m = p.matcher(input); + + if (!m.matches()) { + return null; + } + result.maj = Integer.parseInt(m.group(1)); + result.min = Integer.parseInt(m.group(2)); + result.micro = Integer.parseInt(m.group(3)); + if (m.groupCount() == 7) { + result.qualifier = m.group(7); + } else { + result.qualifier = null; + } + return result; + } + + /** + * Emits a org.apache.zookeeper.version.Info interface file with version and + * revision information constants set to the values passed in as command + * line parameters. The file is created in the current directory. <br> + * Usage: java org.apache.zookeeper.version.util.VerGen maj.min.micro[-qualifier] + * rev buildDate + * + * @param args + * <ul> + * <li>maj - major version number + * <li>min - minor version number + * <li>micro - minor minor version number + * <li>qualifier - optional qualifier (dash followed by qualifier text) + * <li>rev - current Git revision number + * <li>buildDate - date the build + * </ul> + */ + public static void main(String[] args) { + if (args.length != 3) + printUsage(); + try { + Version version = parseVersionString(args[0]); + if (version == null) { + System.err.println( + "Invalid version number format, must be \"x.y.z(-.*)?\""); + System.exit(1); + } + String rev = args[1]; + if (rev == null || rev.trim().isEmpty()) { + rev = "-1"; + } else { + rev = rev.trim(); + } + generateFile(new File("."), version, rev, args[2]); + } catch (NumberFormatException e) { + System.err.println( + "All version-related parameters must be valid integers!"); + throw e; + } catch (IOException e) { + System.out.println("Unable to generate version.Info file: " + + e.getMessage()); + System.exit(1); + } + } + +} http://git-wip-us.apache.org/repos/asf/zookeeper/blob/7291e47c/zookeeper-server/src/main/resources/lib/cobertura/README.txt ---------------------------------------------------------------------- diff --git a/zookeeper-server/src/main/resources/lib/cobertura/README.txt b/zookeeper-server/src/main/resources/lib/cobertura/README.txt new file mode 100644 index 0000000..f5ba88f --- /dev/null +++ b/zookeeper-server/src/main/resources/lib/cobertura/README.txt @@ -0,0 +1,3 @@ +Download the cobertura binary from the following location and unpack it into this directory. Run "cobertura-report" target from build.xml to generate coverage report. + +http://cobertura.sourceforge.net/download.html