`

java ldap开发常见问题与代码

    博客分类:
  • java
 
阅读更多

最近因工作需要自己花时间学习了ldap的API并在项目中使用了,其中ldap开发有自己的API,现在的java自带的API也包含了相关的API

ldap自身的API:https://www.novell.com/documentation/developer/jldap/jldapenu/api/

jdk自带的api在javax.naming.*包下面
其中openldap有些比较坑的地方,页面上创建Entry和修改Entry的时候的字段名不一致,然后使用java开发的时候又不一致
其中添加用户的时候需要添加属性(objectClass=posixAccount)
添加组的时候需要添加属性(objectClass=posixGroup)
其中在使用java添加的时候javaAPI中需要添加的属性与openldap页面添加的属性名对应关系有
User Name 对应属性 uid
Password 对应属性 userPassword
如果不一致会报如下错误error code 17 - User Name :AttributeDescription contains in appropriate characters
添加组有gid,添加用户有uid,通过页面添加的时候可以发现他们的id应该是自增的,但是你查出来之后,设置属性必须使用字符串设置进去,否则会报Malformed gidNumber 错误,当然这个属性名也是页面上的
下面是通过javaAPI写的示例
package com.java.ldap;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class LdapTest {
	private static final String BASE_DN = "dc=csair,dc=com";
	private static final String USER_DN_BASE = "ou=users,dc=csair,dc=com";
	private static final String GROUP_DN_BASE = "ou=group,dc=csair,dc=com";
	
	public static void main(String[] args) throws Exception {
		LdapContext connectLdap = connectLdap();
		
		getMaxId(connectLdap);
		 SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);
		NamingEnumeration<SearchResult> search = connectLdap.search("cn=athenatest111,ou=group,dc=abc,dc=com", "(objectClass=posixGroup)", null, searchCtls);
//		NamingEnumeration<SearchResult> search = connectLdap.search("cn=caifan,ou=users,dc=abc,dc=com", "(objectClass=posixAccount)", null,searchCtls);
		while (search.hasMore()) {
			SearchResult result = search.next();
			//System.out.println(result.getAttributes().get("memberUid").get(2));//.get("description")
			System.out.println(result.getAttributes().get("gidnumber").get());//.get("description")
			System.out.println(result.getAttributes().get("objectClass"));
			System.out.println(result.getName());
		}
		getAllGroups(connectLdap);
	}
	
	public static void getAllGroups(LdapContext context) throws Exception {
            SearchControls searchCtls = new SearchControls();
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

            //SHIRO-115 - prevent potential code injection:
            String searchFilter = "(&(objectClass=posixGroup))";
            NamingEnumeration answer = context.search("ou=group,dc=abc,dc=com", searchFilter, null, searchCtls);

            while (answer.hasMoreElements()) {
                SearchResult searchResult = (SearchResult) answer.next();

                String group = searchResult.getName().substring(3);

                //组名
                String groupName = "";
                Attribute groupNameAttr = searchResult.getAttributes().get("description");
               System.out.println(group + "::" + groupNameAttr + "::" + searchResult.getAttributes().get("memberUid"));

                //组成员
                List<String> userIds = new ArrayList<>();
                Attribute memberUidAttr = searchResult.getAttributes().get("memberUid");
                if (memberUidAttr != null) {
                    NamingEnumeration memberUid = memberUidAttr.getAll();
                    while (memberUid.hasMore()) {
                        String userId = (String) memberUid.next();
                        userIds.add(userId);
                    }
                }

            }
        
    }

	
	public static Integer getMaxId(LdapContext context) throws Exception {
		SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        List<Integer> idList = new ArrayList<>();
        
        //NamingEnumeration<SearchResult> search = context.search("ou=group,dc=abc,dc=com", "(objectClass=posixGroup)", null,searchCtls);
        NamingEnumeration<SearchResult> search = context.search("ou=users,dc=abc,dc=com", "(objectClass=posixAccount)", null,searchCtls);
        while (search.hasMore()) {
			SearchResult result = search.next();
			//System.out.println(result.getAttributes().get("memberUid").get(2));//.get("description")
			//System.out.println(result.getAttributes().get("gidnumber").get());//.get("description")
			
			idList.add(Integer.parseInt(result.getAttributes().get("uidnumber").get().toString()));
			//System.out.println(result.getAttributes().get("uidnumber").get());
		}
        Collections.sort(idList);
        for(Integer id : idList) {
        	System.out.println(id);
        }
        System.out.println("maxID:" + idList.get(idList.size() -1));
		return idList.get(0);
	}
	
	public static LdapContext connectLdap() throws Exception {
		// 连接Ldap需要的信息  
	    String ldapFactory = "com.sun.jndi.ldap.LdapCtxFactory";  
	    String ldapUrl = "ldap://ip:389";// url  
	    String ldapAccount = "cn=admin,dc=abc,dc=com"; // 用户名  
	    String ldapPwd = "password";//密码  
	    Hashtable env = new Hashtable();  
	    env.put(Context.INITIAL_CONTEXT_FACTORY, ldapFactory);  
	    // LDAP server  
	    env.put(Context.PROVIDER_URL, ldapUrl);  
	    env.put(Context.SECURITY_AUTHENTICATION, "simple");  
	    env.put(Context.SECURITY_PRINCIPAL, ldapAccount);  
	    env.put(Context.SECURITY_CREDENTIALS, ldapPwd);  
	    env.put("java.naming.referral", "follow");  
	    LdapContext ctxTDS = new InitialLdapContext(env, null);  
	    return ctxTDS;  
	}
	
	
	 public void addUser(String cn, String givenName, String password, List<String> group) throws NamingException {
	        LdapContext ldapContext = null;
	        try {
	            ldapContext = connectLdap();
	            Integer uidNumber = getMaxId(ldapContext, USER_DN_BASE) + 1;
	            String md5 =  generateMD5(password);
	            Attributes attributes = new BasicAttributes();
	            Attribute passwordAttribute = new BasicAttribute("userPassword", md5);
	            Attribute objectClass = new BasicAttribute("objectClass", true);
	            objectClass.add("inetOrgPerson");
	            objectClass.add("posixAccount");
	            objectClass.add("top");

	            Attribute cnAttr = new BasicAttribute("cn",cn);
	            Attribute givenNameAttr = new BasicAttribute("givenName",givenName);
	            Attribute homeDirectoryAttr = new BasicAttribute("homeDirectory", "/home/users/" + cn);
	            Attribute loginShellAttr = new BasicAttribute("loginShell", "/bin/sh");
	            Attribute snAttr = new BasicAttribute("sn", cn);
	            Attribute uidAttr = new BasicAttribute("uid", cn);
	            Attribute uidNumberAttr = new BasicAttribute("uidNumber", uidNumber + "");

	            SearchControls searchCtls = new SearchControls();
	            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
	            if(group.size() > 0) {
	                NamingEnumeration<SearchResult> searchGroup = ldapContext.search("cn=" + group.get(0) + "," + GROUP_DN_BASE, "(objectClass=posixGroup)", null, searchCtls);
	                while (searchGroup.hasMore()) {
	                    String gid = searchGroup.next().getAttributes().get("gidnumber").get().toString();
	                    Attribute gidAttr = new BasicAttribute("gidNumber", gid);
	                    attributes.put(gidAttr);
	                }
	                //添加用户到组
	                group.forEach(groupName -> {
	                    try {
	                        moveUser2Group(cn, groupName, 0);
	                    } catch (Exception e) {
	                    }
	                });
	            }
	            attributes.put(cnAttr);
	            attributes.put(givenNameAttr);
	            attributes.put(passwordAttribute);
	            attributes.put(objectClass);
	            attributes.put(loginShellAttr);
	            attributes.put(snAttr);
	            attributes.put(homeDirectoryAttr);
	            attributes.put(uidAttr);
	            attributes.put(uidNumberAttr);

	            ldapContext.createSubcontext("cn=" + cn + "," + USER_DN_BASE, attributes);
	        } catch (Exception e) {
	        	e.printStackTrace();
	        } finally {
	            if(ldapContext != null) {
	                ldapContext.close();
	            }
	        }

	    }

	    /**
	     * * 其中givenName sn UserName在修改的时候可以添加成多个值,添加的时候是通过其他值进行转换的,其中User Name sn在修改的时候最后至少保留一个,否则报错
	     * @param cn 用户唯一标识
	     * @param givenName 添加的givenName属性
	     * @param addGroup 添加的组
	     * @param subGroup 减少的组
	     */
	    public void modifyUser(String cn, String givenName, List<String> addGroup, List<String> subGroup) {
	        LdapContext ldapContext = null;
	        try {
	            ldapContext = connectLdap();
	            Attributes attributes = new BasicAttributes();
	            SearchControls searchControls = new SearchControls();
	            searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
	            NamingEnumeration<SearchResult> search = ldapContext.search("cn=" + cn + "," + USER_DN_BASE, "(objectClass=posixAccount)", null, searchControls);
	            while (search.hasMore()) {
	                SearchResult searchResult = search.next();
	                Attributes beforeAttrs = searchResult.getAttributes();
	                Attribute givenNameAttrs = beforeAttrs.get("givenName");
	                if(givenNameAttrs != null && givenNameAttrs.get() != null && !givenNameAttrs.get().toString().trim().equals(givenName.trim())) {
	                    givenNameAttrs.clear();
	                    givenNameAttrs.add(givenName);
	                    attributes.put(givenNameAttrs);
	                } else {
	                    givenNameAttrs = new BasicAttribute("givenName", givenName);
	                    attributes.put(givenNameAttrs);
	                }
	                Attribute uidAttrs = beforeAttrs.get("uid");

	                if(addGroup != null && !addGroup.isEmpty()) {
	                    if(addGroup.get(0).trim().length() > 0) {
	                        NamingEnumeration<SearchResult> searchResults = ldapContext.search("cn=" + addGroup.get(0) + "," + GROUP_DN_BASE, "(objectClass=posixGroup)", null, searchControls);
	                        Attribute gidAttribute = null;
	                        while (searchResults.hasMore()) {
	                            SearchResult sr = searchResults.next();
	                            gidAttribute = sr.getAttributes().get("gidNumber");
	                            attributes.put(new BasicAttribute("gidNumber", gidAttribute.get()));
	                        }
	                        addGroup.forEach(add -> {
	                            try {
	                                if (add.trim().length() > 0) {
	                                    moveUser2Group(cn, add, 0);
	                                }
	                            } catch (Exception e) {
	                            }
	                        });
	                    }
	                }
	                if (subGroup != null && subGroup.size() > 0) {
	                    subGroup.forEach(sub -> {
	                        try {
	                            if(sub.trim().length() > 0) {
	                                moveUser2Group(cn, sub, 1);
	                            }
	                        } catch (Exception e) {
	                        }
	                    });
	                }

	                ldapContext.modifyAttributes("cn=" + cn + "," + USER_DN_BASE, DirContext.REPLACE_ATTRIBUTE, attributes);
	            }

	        } catch (Exception e) {
	        } finally {
	            if(ldapContext != null) {
	                try {
						ldapContext.close();
					} catch (NamingException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
	            }
	        }
	    }

	    private void modifyList(Attribute attrs, List<String> addValue, List<String> subValue) {
	        if(attrs != null ) {
	            if(addValue != null && addValue.size() > 0) {
	                for (String add : addValue) {
	                    if (!attrs.contains(add)) {
	                        attrs.add(add);
	                    }
	                }
	            }
	            if(subValue != null && subValue.size() > 0) {
	                for (String sub : subValue) {
	                    if (attrs.contains(sub)) {
	                        attrs.remove(sub);
	                    }
	                }
	            }
	        }
	    }

	    public void deleteUser(String username) throws Exception {
	        LdapContext context = null;
	        try {
	            context = connectLdap();
	            context.destroySubcontext("cn=" + username + "," + USER_DN_BASE);
	        } catch (NamingException e) {
	        } finally {
	            if(context != null) {
	                context.close();;
	            }
	        }

	    }
	    /**
	     * 添加用户到指定的组
	     * @param userId
	     * @param groupName
	     * @param type 0:添加 1:移除
	     * @throws Exception 
	     */
	    private void moveUser2Group (String userId, String groupName, Integer type) throws Exception {
	        LdapContext context = connectLdap();
	        String searchFilter = "(&(objectClass=posixGroup))";
	        SearchControls searchCtrl = new SearchControls();
	        searchCtrl.setSearchScope(SearchControls.OBJECT_SCOPE);
	        NamingEnumeration answer = context.search("cn=" + groupName + "," + GROUP_DN_BASE, searchFilter, null, searchCtrl);
	        SearchResult searchResult;
	        while (answer.hasMore()) {
	            searchResult = (SearchResult) answer.next();
	            Attribute attribute = searchResult.getAttributes().get("memberUid");
	            if(attribute == null) {
	                attribute = new BasicAttribute("memberUid");
	            }
	            if (type == 0 && !attribute.contains(userId)) {
	                attribute.add(attribute.size(), userId);
	            } else if(type == 1 && attribute.contains(userId)) {
	                attribute.remove(userId);
	            }
	            ModificationItem[] item = new ModificationItem[1];
	            item[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attribute);
	            context.modifyAttributes("cn=" + groupName + "," + GROUP_DN_BASE, item);
	        }
	    }

	    private Integer getMaxId(LdapContext context, String dn) throws Exception {
	        SearchControls searchCtls = new SearchControls();
	        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
	        List<Integer> idList = new ArrayList<>();
	        NamingEnumeration<SearchResult> search = null;
	        if(GROUP_DN_BASE.equals(dn)) {
	            search = context.search(GROUP_DN_BASE, "(objectClass=posixGroup)", null,searchCtls);
	            while (search.hasMore()) {
	                SearchResult result = search.next();
	                idList.add(Integer.parseInt(result.getAttributes().get("gidnumber").get().toString()));
	            }
	        } else if(USER_DN_BASE.equals(dn)) {
	            search = context.search(USER_DN_BASE, "(objectClass=posixAccount)", null,searchCtls);
	            while (search.hasMore()) {
	                SearchResult result = search.next();
	                idList.add(Integer.parseInt(result.getAttributes().get("uidnumber").get().toString()));
	            }
	        }

	        Collections.sort(idList);
	        return idList.get(idList.size() -1);
	    }
	    
	    public static String generateMD5(String password) {
			//声明StringBuffer对象来存放最后的值
		      StringBuffer sb=new StringBuffer();
			 try {
			      //1.初始化MessageDigest信息摘要对象,并指定为MD5不分大小写都可以
			      MessageDigest md=MessageDigest.getInstance("md5");
			       //2.传入需要计算的字符串更新摘要信息,传入的为字节数组byte[],
			      //将字符串转换为字节数组使用getBytes()方法完成
			      //指定时其字符编码 为utf-8
			      md.update(password.getBytes("utf-8"));
			       //3.计算信息摘要digest()方法
			      //返回值为字节数组
			      byte [] hashCode=md.digest();
			       //4.将byte[] 转换为找度为32位的16进制字符串
			        
			      //遍历字节数组
			      for(byte b:hashCode){
			        //对数组内容转化为16进制,
			        sb.append(Character.forDigit(b>>4&0xf, 16));
			        //换2次为32位的16进制
			        sb.append(Character.forDigit(b&0xf, 16));
			      }
			    } catch (NoSuchAlgorithmException e) {
			      e.printStackTrace();
			    } catch (UnsupportedEncodingException e) {
			      e.printStackTrace();
			    }
			 return sb.toString();
		}
}
 
后面通过ldapAPI写示例
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.novell.ldap.util.DN;

/**
 * 通过LDAPConnection获取词条
 * @author admin
 *
 */
public class LdapUtils {
	private static String ldapHost = "IP";
	private static int ldapPort = 389;  
	private static String ldapBindDN = "cn=admin,dc=abc,dc=com";
	private static String ldapPassword = "password";  
	private static LDAPConnection connection = null; 
	
	public static void main(String[] args) throws Exception {
		DN dn = new DN(ldapBindDN);
		int countResults = 0;
		openConnection();
		//LDAPSearchResults results = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_BASE, "objectClass=*", null, false);//获取当前DN级别LDAPEntry
		//LDAPSearchResults results1 = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_ONE, "objectClass=*", null, false);//获取当前DN级别的子集LDAPEntry
		LDAPSearchResults results2 = connection.search(dn.getParent().toString(), LDAPConnection.SCOPE_SUB, "objectClass=*", null, false);//获取所有DN级别的子集
		System.out.println("===results1======");
		while(results2.hasMore()) {
			System.out.println(results2.next());
			countResults++;
			
		}
		
		System.out.println(countResults);
		
		connection.disconnect();
	}
	
	/**
	 * 添加LDAPEntry
	 * @param connection
	 * @param dn
	 * @param attrList
	 * @throws LDAPException
	 */
	public void addLDAPEntry(LDAPConnection connection, String dn, List<LDAPAttribute> attrList) throws LDAPException {
		LDAPAttributeSet ldapAttributeSet = new LDAPAttributeSet();
		ldapAttributeSet.addAll(attrList);
		LDAPEntry ldapEntry = new LDAPEntry(dn, ldapAttributeSet);
		connection.add(ldapEntry);
		connection.disconnect();
	}
	
	/**
	 * 修改LDAP属性
	 * @param connection
	 * @param dn
	 * @param attrList
	 * @param type LDAPModification。REPLACE DELETE
	 * @throws LDAPException
	 */
	public static void modifyLDAPEntryAttr(LDAPConnection connection, String dn, List<LDAPAttribute> attrList, int type) throws LDAPException {
		LDAPModification ldapModification;
		if(attrList != null && attrList.size() > 0) {
			for(LDAPAttribute attr : attrList) {
				ldapModification = new LDAPModification(type, attr);
				connection.modify(dn, ldapModification);
			}
		}
	}
	
	
	/** 
	 * 连接LDAP
	 */
	public static void openConnection() {
		if (connection == null) {
			try {
				connection = new LDAPConnection();
				connection.connect(ldapHost, ldapPort);
				connection.bind(LDAPConnection.LDAP_V3, ldapBindDN, ldapPassword.getBytes("UTF8"));
			} catch (Exception e) {
				System.out.println("连接LDAP出现错误:\n" + e.getMessage());
				System.exit(1);
			}
		}
	}
	
	/**
	 * 根据查询scope 获取所有的子LDAPEntry  scope:LDAPConnection.SCOPE_BASE(查询当前级LdapEnttry)  LDAPConnection.SCOPE_ONE(查询当前子集LDAPEntry)  LDAPConnection.SCOPE_SUB(查询所有的子集LDAPEntry,如有子目录则递归)
	 * @param dn
	 * @param connection
	 * @param searchFilter 搜索条件  通常为"objectClass=*"
	 * @param attrs 属性
	 * @return
	 * @throws LDAPException
	 */
	public static Map<String, Object> getLDAPEntry(String dn, LDAPConnection connection, int scope, String searchFilter, String[] attrs) throws LDAPException {
		Map<String, Object> resultsMap = new HashMap<>();
		List<LDAPEntry> entryList = new ArrayList<>();
		int resultsCounts = 0;
		LDAPSearchResults ldapSearchResults = connection.search(dn, scope, searchFilter, attrs, false);
		
		LDAPEntry ldapEntry;
		while(ldapSearchResults.hasMore()) {
			ldapEntry = ldapSearchResults.next();
			entryList.add(ldapEntry);
			resultsCounts++;
		}
		resultsMap.put("ldapEntryCounts", resultsCounts);
		resultsMap.put("ldapEntryList", entryList);
		return resultsMap;
	}
	
		
	/**
	 * 根据连接和DN获取LDAPEntry
	 * @param connection
	 * @param ldapBindDN
	 * @return
	 * @throws Exception
	 */
	public static LDAPEntry getLDAPEntry(LDAPConnection connection, String ldapBindDN) throws Exception {
		return connection.read(ldapBindDN);
	}
	
	
	/**
	 * @param connection
	 * @param userDN
	 * @param groupDN
	 * @return
	 * 添加用户到组
	 * @throws LDAPException 
	 */
	public static boolean addUser2Group(LDAPConnection connection, String userDN, String groupDN, List<LDAPAttribute> userAttributes, List<LDAPAttribute> groupAtrributes) {
		try {
			modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.ADD);
		} catch (LDAPException e) {
			try {
				modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.DELETE);
			} catch (LDAPException e1) {
				e1.printStackTrace();
			}
			System.out.println(e.getMessage());
			return false;
		}
		try {
			modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.ADD);
		} catch (LDAPException e) {
			try {
				modifyLDAPEntryAttr(connection,userDN, userAttributes, LDAPModification.DELETE);
			} catch (LDAPException e1) {
				e1.printStackTrace();
			}
			System.out.println(e.getMessage());
			return false;
		}
		
		return true;
		
	}
	
	
	
}
 
 
有疑问欢迎加入:513650703群聊,有更好的意欢迎提出分享。
 
 
分享到:
评论

相关推荐

    JAVA上百实例源码以及开源项目源代码

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    JAVA上百实例源码以及开源项目

     Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。 递归遍历矩阵 1个目标文件,简单! 多人聊天室 3...

    java开源包10

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包8

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    JAVA_API1.6文档(中文)

    java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....

    java开源包4

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包11

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包6

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包101

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包9

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包5

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包1

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包3

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    Java 1.6 API 中文 New

    java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....

    java开源包2

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java开源包7

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    Java资源包01

    J2C 将 Java 代码转成 C++ 代码,这是源码级别的转换,输出的 C++ 代码是有效的代码。 OSGi 分布式通讯组件 R-OSGi R-OSGi 是一套适用于任意满足 OSGi 架构的分布式通讯组件。它以 jar 的形式发布,部署容易,使用...

    java api最新7.0

    java.awt.im.spi 提供启用可以与 Java 运行时环境一起使用的输入方法开发的接口。 java.awt.image 提供创建和修改图像的各种类。 java.awt.image.renderable 提供用于生成与呈现无关的图像的类和接口。 java.awt....

Global site tag (gtag.js) - Google Analytics