spring ldap简单的例子(四)
2016-04-28LDAP攻城狮38456°c
A+ A-好了通过前面的文章已经可以用程序实现对LDAP的添加了,下面将继续进阶,通过程序读取Excel中的数据插入到LDAP中,说白了就是批量操作,看懂前面两个例子的基本也可以想来怎样进行导入,所以我这里稍加改动,我先把需求阐述一下,明白我们要做什么,以及为什么要用这篇文章中的这种方式来做。
简单分析:每个企业中都会有各种各样的部门,并且很多部门还会包含子部门,子部门还会继续包含子部门这样的,那么我们在导入部门节点的时候就必须要知道自己是属于哪一个节点的,只有知道了自己是属于哪一个节点的才能正确的导入组织结构。那么我们在拿到这份表格后,首先肯定是要自己对表格进行整理的,因为用户给你的表格你也不是拿过来就可以用的,通过你的整理,方便你程序对这个表格的操作。下面这张图片是对表格的加工:
这个表格中真正要导入的数据是:显示顺序、单位名称、单位简称、备注信息、单位编码,其他字段都是辅助字段。部门间的从属关系是我随便写的,
显示顺序,是用来标识在同一节点下的所有部门谁在上面,谁在下面,一般企业可能不会太在意这些,但是一些大的企业,尤其是央企,对这个是有要求的;
单位级别,是用来标识自己是几级单位,3级单位肯定会在某一个2级单位下,以此类推。
上级单位编码,这个一般来说你在收集数据的时候客户会提供给你,每个部门都知道自己的上级部门是谁,自己是直属还是隶属这个他们自己当然很清楚,在这个表里面,我们约定,是一级单位的就是没有上级单位,那么上级单位这一栏就为空,图中最下面的资金管理部是三级单位,那么他的上级单位就是投资管理部,再上一级单位就是市场经营部。
好了明白了这些前提,我们将这张表导入LDAP,
对于导入部门,我们使用实体来导入,为什么要使用实体呢,因为我们需要程序找出每个部门的上级部门,需要使用递归来完成,将没一个单位实例化为一个实体会很方便操作。但是使用实体的弊端是不够灵活,如果模板有改动,就需要改动对应的实体和属性添加部分。相对于人员来说,部门应该说还是可以使用实体的方式导入的,只要模板规定好了,变动一般不会太大。
好了建立实体,新添加一个包com.study.domain,实体命名为Ou.java
package com.study.domain; public class Ou { private String order;//显示顺序 private String orgName;//单位名称 private String simpOrgName;//单位简称 private String supOrgName;//上级单位 private String remark;//备注 private int orgGrade;//单位级别 private String orgNumber;//单位编码 private String supOrgNumber;//上级单位编码 //...get和set方法省略了,请自行添加 }
然后在修改接口OuDAO.java,添加导入部门时的方法定义:
package com.study.dao; import java.util.Map; import com.study.domain.Ou; public interface OuDAO { /** * 根据DN值插入单位 * @param map * @param supDn */ public void insertOu(Map<String,String> map,String supDn); /** * 通过单位实体和节点DN添加单位 * @param o * @param supDn */ public void insertOu(Ou o, String supDn); }
接下来在实现类中添加对该方法的实现,为了方便起见,我贴出的是全部的代码,
package com.study.dao.impl; import java.util.Date; import java.util.Map; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import org.apache.log4j.Logger; import org.springframework.ldap.NameAlreadyBoundException; import org.springframework.ldap.core.DistinguishedName; import org.springframework.ldap.core.LdapTemplate; import com.study.dao.OuDAO; import com.study.domain.Ou; public class OuDAOImpl implements OuDAO { LdapTemplate ldapTemplate; public void setLdapTemplate(LdapTemplate ldapTemplate){ this.ldapTemplate=ldapTemplate; } Logger logger=Logger.getLogger(OuDAOImpl.class); @Override public void insertOu(Map<String, String> map, String supDn) { Attributes ouAttributes=new BasicAttributes(); BasicAttribute ouBasicAttribute=new BasicAttribute("objectclass"); ouBasicAttribute.add("organizationalUnit"); ouAttributes.put(ouBasicAttribute); for(String str:map.keySet()){ ouAttributes.put(str,map.get(str)); tactDN,null,ouAttributes); } @Override public void insertOu(Ou o, String supDn) { Attributes ouAttributes=new BasicAttributes(); BasicAttribute ouBasicAttribute=new BasicAttribute("objectclass"); ouBasicAttribute.add("organizationalUnit"); ouAttributes.put(ouBasicAttribute); ouAttributes.put("deptCode",o.getOrgNumber()); ouAttributes.put("description",o.getRemark()); //除了对象本身的属性外,手动为这个部门添加一个创建时间 ouAttributes.put("createTime",new Date(System.currentTimeMillis()).toLocaleString()); DistinguishedName newCon\=new DistinguishedName(supDn); newContactDN.add("ou",o.getOrgName()); try{ ldapTemplate.bind(newContactDN,null,ouAttributes); }catch(NameAlreadyBoundException exception){ logger.warn(o.getOrgNumber()+"--"+exception.getExplanation()); } } }
我在代码中象征性的添加进去了几个属性,对于低46行的这个属性cretatTime来说,我们首先要在core.schema中添加定义,添加定义如下:
attributetype ( 2.5.4.71 NAME 'createTime' DESC 'X.520(4th): pseudonym for the object' SUP name )
并且在objectclass中添加这个属性:
objectclass ( 2.5.6.5 NAME 'organizationalUnit' DESC 'RFC2256: an organizational unit' SUP top STRUCTURAL MUST ou MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description $ sslvpn $ incontroller $ deptcode $ createTime ) )
好,接下来是测试类了,在com.study.ou下新建类ImportOuForXls.java,代码如下:
package com.study.ou; import java.io.FileInputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import com.study.dao.OuDAO; import com.study.domain.Ou; @SuppressWarnings("deprecation") public class ImportOuForXls implements Comparator<Ou>{ static Map<String,Ou> map=new HashMap<String,Ou>(); Resource resource=new ClassPathResource("com/study/ou/ldap-ou.xml"); BeanFactory factory=new XmlBeanFactory(resource); OuDAO ldapOu=(OuDAO) factory.getBean("ldapContext"); public void insertOuMethod(String src) throws Exception{ InputStream ins=new FileInputStream(src); //创建HSSFWork工作薄 HSSFWorkbook hssfWorkbook=new HSSFWorkbook(ins); hssfWorkbook.setMissingCellPolicy(Row.CREATE_NULL_AS_BLANK); List<Ou> list=new ArrayList<Ou>(); //获得需要解析的sheet Sheet sheet = sheet=hssfWorkbook.getSheetAt(0); for(Row row:sheet){ if(row.getRowNum()==0){ continue; } //从第二行开始解析,row.getLastCellNum()有效列数 for(int a=0;a<row.getLastCellNum();a++){ row.getCell(a).setCellType(Cell.CELL_TYPE_STRING); } Ou organ=new Ou(); organ.setOrder(row.getCell(0).getStringCellValue()); organ.setOrgName(row.getCell(1).getStringCellValue()); organ.setSimpOrgName(row.getCell(2).getStringCellValue()); organ.setSupOrgName(row.getCell(3).getStringCellValue()); organ.setRemark(row.getCell(4).getStringCellValue()); organ.setOrgGrade(Integer.parseInt(row.getCell(5).getStringCellValue())); organ.setOrgNumber(row.getCell(6).getStringCellValue()); organ.setSupOrgNumber(row.getCell(7).getStringCellValue().length()>1?row.getCell(7).getStringCellValue():"sup***"); list.add(organ); map.put(organ.getOrgNumber(), organ); } ImportOuForXls or=new ImportOuForXls(); Collections.sort(list, or); Map<String,Ou> supa=new HashMap<String,Ou>(); List<Ou> remList=new ArrayList<Ou>(); for(Ou o:list){ //这里先找出所有的一级父节点,在这里可以完成一级节点的添加 if(o.getSupOrgNumber().equals("sup***")){ supa.put(o.getOrgNumber(), o); remList.add(o); ldapOu.insertOu(o, ""); System.out.println(o.getOrder()+","+o.getOrgGrade()+","+o.getOrgName()+","+o.getSupOrgName()); } } list.removeAll(remList); System.out.println(list.size()); Collections.sort(list,or); for(Ou o:list){ getSupOu(o); System.out.println(o.getOrgGrade()+","+o.getOrgName()+",父节点:"+supDnValue+",应当添加的DN值为:ou="+o.getOrgNumber()+","+supDnValue); ldapOu.insertOu(o, (supDnValue.toString()).substring(0, supDnValue.length()-1)); supDnValue.delete(0, supDnValue.length()); } } static StringBuffer supDnValue=new StringBuffer(); //找出父节点 public void getSupOu(Ou o){ if(!o.getSupOrgNumber().equals("sup***")){ supDnValue.append("ou="); supDnValue.append(map.get(o.getSupOrgNumber()).getOrgName()); supDnValue.append(","); getSupOu(map.get(o.getSupOrgNumber())); } } public int compare(Ou o1, Ou o2) { Ou a1=o1; Ou a2=o2; int flag=String.valueOf(a1.getOrgGrade()).compareTo(String.valueOf(a2.getOrgGrade())); if(flag==0){ return String.valueOf(a1.getOrgGrade()).compareTo(String.valueOf(a2.getOrgGrade())); }else{ return flag; } } public static void main(String[] args) { try { new ImportOuForXls().insertOuMethod("ou.xls"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
通过POI读取表格中的数据,由于我们最后一列是上级单位,存在一种可能是这个单位本身就没有上级单位的,那么我们在这里使用一个三元表达式,将一级节点用"sup***"来标识,然后后面程序通过这个标识来判定当前节点是不是顶级节点。当前这个类实现了接口Comparator并且重写了方法 compare,这个方法通过表格中的单位级别将所有元素排序然后在65行的地方通过一个for循环与sup***标识结合找出所有的1级节点,并且执行添加,然后再将这个一级节点放入一个list中,到74行的时候原先会从list中移除所有的一级节点,要知道,我们在读取表格的时候是放了两份的,一份是存在map中,一份是存在list中,这个map可以看做暂时的一个数据库,后面的程序都是在操作这个map,包括找父节点这样的操作都是基于这个map的。
紧接着在第77行的时候再次对list进行排序,那么从这个list中读取节点的时候首先读取到的就是所有的2级节点了,然后使用一个增强for循环来遍历这个list,在78行这里,我们调用一个递归函数,这个递归函数会找出当前节点的父节点以及父节点的父节点,并且将它组装成一个符合LDAP规范的DN值,在LDAP中要插入一个节点,必须要知道当前节点的父节点的DN值。
以上面那个表格为例:
我要插入资金管理部,那么父节点的DN值就是:ou=tzglb,ou=scjy
是倒着写的,上面第86行的递归函数会组装出符合这样规范的DN值放入supDnValue,在第81行执行完添加后紧接着将这个supDnValue清空,进入下一次循环
执行添加的部分很简单了,相信都可以看懂了。
这个导入部门写的不够完美的地方呢就是添加的方法,里面的属性都是写死的,要是要更改属性的话就需要修改这里与部门的属性相对应。
那么在下一篇文章中呢,我会对这一部分进行优化,程序不用改动,通过你表格里的内容自动调整属性值,当然这个也是有前提的,守恒定律还是要遵循的,程序不动那就要表格动咯!
PS:
1、可能写的比较乱,凑合看了。
2、上面这个例子是可以跑通的。
3、如果有报错,仔细看报错的原因,我觉得这个应该说是比较容易找到的,spring ldap的报错还算是易懂了。
4、最后一点细节,表格的属性一定最好都是文本类型: