Uma biblioteca de APIs do CMIS para Python, Parte 1: Apresentando o cmislib

Uma API de Content Management Interoperability Services do lado do cliente

Esta é a primeira de uma série em duas partes que irá apresentar o cmislib, uma biblioteca do lado do cliente para trabalhar com as bibliotecas de conteúdo do CMIS. O Content Management Interoperability Services (CMIS) é uma especificação que fornece uma forma padrão de acessar conteúdo, independentemente da implementação do repositório subjacente ou da escolha da linguagem de programação de front-end. Neste artigo, você aprenderá sobre a API cmislib do Python usando exemplos.

Jeff Potts, Author, Optaros

Photo of Jeff PottsDennis Sosnoski é consultor e instrutor especializado em serviços XML e da Web baseados em Java. Possui experiência profissional de mais de 30 anos no desenvolvimento de software, tendo focado seu trabalho nos últimos 10 anos em tecnologias XML e Java no lado do servidor. Dennis é o desenvolvedor líder da estrutura de software livre JiBX XML Data Binding e da estrutura de serviços da Web associada JiBX/WS. Ele também é committer na estrutura de serviços da Web Apache Axis2. Também foi um dos membros do Expert Group para as especificações JAX-WS 2.0 e JAXB 2.0. O material da série Java Web Services tem como base as aulas do treinamento de Dennis.



09/Abr/2010

Apresentando o cmislib: Uma API do CMIS para Python

Parece que esta será uma primavera intensa para a especificação Content Management Interoperability Services (CMIS). O OASIS está preparando a especificação para seu release 1.0, fornecedores de repositórios estão trabalhando intensamente para aprontar implementações do lado do servidor e desenvolvedores da comunidade de gerenciamento de conteúdo estão lançando clientes e APIs que facilitam a exploração e o trabalho com repositórios de conteúdo detalhado de maneira padrão.

Siglas usadas com frequência

  • ACL: Access control list
  • API: Application program interface
  • HTTP: Hypertext Transfer Protocol
  • OASIS: Organization for the Advancement of Structured Information Standards
  • REST: Representational State Transfer
  • SDK: Software development kit
  • SQL: Structured Query Language
  • URL: Uniform Resource Locator
  • WSDL: Web Services Description Language
  • XML: Extensible Markup Language

Se você alguma vez já construiu um aplicativo centralizado em conteúdo, sabe que, tipicamente, a primeira dificuldade é descobrir como se comunicar com o repositório de conteúdo subjacente. Primeiramente, sua equipe recebe um curso intensivo de SDK do repositório. Em seguida, você projeta o aplicativo, incluindo a integração entre a camada de apresentação e a camada de serviços de conteúdo. Finalmente, você executa seu plano e comemora o sucesso no final da tarde com batatas fritas com queijo. O problema com isso, porém, além do impacto desses carboidratos em sua cintura, é que o processo se repete para cada nova combinação de front-end e backend, porque cada repositório tem sua própria API exclusiva. E se seu aplicativo se comunicar com vários repositórios, como geralmente é o caso, você terá de aprender e codificar para várias interfaces.

Felizmente, esse problema já foi resolvido antes. O padrão é o mesmo que existia antes da padronização de SQL. Os bancos de dados relacionais criados pela IBM® e outros começaram a aparecer no início da década de 1970, mas o primeiro esforço de criação de padrões formais em torno da linguagem de consulta SQL não aconteceu até 1986. Quando isso aconteceu, principalmente após uma revisão importante em 1992, os desenvolvedores puderam criar aplicativos front-end e tiveram a garantia razoável de que isso poderia funcionar com vários backends relacionais. O CMIS tem o potencial de realizar a mesma coisa para aplicativos centralizados em conteúdo que o SQL fez para os aplicativos de bancos de dados relacionais. Ele fornece uma forma padrão de interagir com o backend, independentemente da implementação do repositório subjacente ou da escolha da linguagem de programação de front-end. Dessa vez, em vez de linhas e colunas, estamos falando sobre conteúdo estruturado e semiestruturado—geralmente arquivos de algum tipo—que existem tipicamente em uma estrutura hierárquica de pastas.

Figura 1. O CMIS fornece uma interface comum independentemente do front-end ou backend que contenha uma imagem
O CMIS fornece uma interface comum independentemente do front-end ou backend que contenha uma imagem

Este artigo aborda uma biblioteca do lado do cliente para trabalhar com repositórios CMIS do Python chamada cmislib. Agora gerenciada como parte do projeto Apache Chemistry, o objetivo da biblioteca é facilitar para que os desenvolvedores de Python escrevam aplicativos centralizados em conteúdo que podem trabalhar com qualquer backend compatível com CMIS. Para muitos, a API pode ser uma maneira fácil de entender o poder do CMIS em primeira mão.

Vamos analisar por que a API foi criada, o que ela faz por você, como foi desenvolvida (caso deseje escrever uma em seu idioma favorito, dica, dica) e alguns exemplos breves da cmislib em ação. O próximo artigo da série irá conduzi-lo por uma aplicação da biblioteca no mundo real.

Motivação para criar a API

Devido a vários motivos, a criação de uma API do lado do cliente para o CMIS no Python parecia uma boa idéia. Alguns deles são estratégicos e idealistas. Outros são um pouco mais táticos e egoístas. Vamos começar com motivos classificados como "Bem Maior".

Provedores compatíveis com CMIS oferecem uma ligação de serviços da Web e uma ligação RESTful Atom Publishing Protocol (AtomPub). Cada ligação apresenta determinadas vantagens em relação à outra, mas uma diferença está em como os serviços CMIS são descobertos e invocados em diferentes servidores. A ligação dos serviços da Web inclui o arquivo WSDL, que pode ser usado para gerar automaticamente código do cliente. Com pouco mais do que o WSDL do servidor CMIS, é possível gerar sua própria API do lado do cliente, se estiver disposto a usar uma ligação de serviços da Web.

À ligação RESTful AtomPub, por outro lado, falta uma forma padrão de descrever seus serviços. Sendo tranquila, todos os seus serviços são acessados através de URL, mas a especificação CMIS deixa as URLs específicas por conta de cada fornecedor. Por isso, se desejar escrever código que funcionará em todos os provedores compatíveis com CMIS que usam a ligação RESTful AtomPub, você terá um pouco mais de trabalho no lado do cliente. Em vez de repetir esse trabalho nos projetos, faz sentido ter um projeto de software livre para fazer uma vez para todos.

O próximo motivo é a adoção por desenvolvedores de aplicativo e fornecedores de repositórios de conteúdo. Os desenvolvedores podem assistir a webinars e publicações de blog e tráfego do Twitter, mas até colocarem a mão e verem o valor em primeira mão, o CMIS é apenas mais uma palavra de efeito enganosa. Se o software ainda viesse embalado, daria para imaginar o visual de "explosão" de cores vivas proclamando em alto tom, “E agora, o CMIS!”. O Python—limpo, produtivo, fácil de instalar—parece uma excelente opção para quem procura ir além do espalhafato para ver o que CMIS pode realmente fazer por ele e seus aplicativos. A API cmislib protege os desenvolvedores dos detalhes da implementação e fornece uma forma de trabalhar intuitiva, orientada a objetos e interoperável. Possivelmente, os desenvolvedores irão experimentá-lo, gostarão do que veem e começarão a usar o CMIS como a forma padrão para os aplicativos personalizados centralizados em conteúdo de interoperarem com repositórios de conteúdo detalhado, quer esse cliente seja construído em Python ou em alguma outra linguagem.

Se somente um ou dois fornecedores adotarem o CMIS, seu propósito terá sido em vão. Assim, definitivamente faz sentido, para aqueles que beneficiam de um padrão, fazer o possível para impulsionar a adoção pelos fornecedores. A distribuição da cmislib inclui testes de unidade simplesmente como uma melhor prática de desenvolvimento. O conjunto de testes é excelente para ajudar a garantir que a funcionalidade não regrida à medida que a API é desenvolvida e é também uma maneira prática de validar a interoperabilidade de uma maneira que possa ser reproduzida. O que é realmente interessante, porém, é que os testes de unidade funcionam como conjunto de teste para fornecedores. IBM, Alfresco™, OpenText e Nuxeo aproveitaram a cmislib para revelar problemas com suas implementações. Isso não se limitou à cmislib—os fornecedores usaram todos os tipos de clientes e ferramentas CMIS construídos pela comunidade para validar seu trabalho e isso é uma coisa muito boa.

Um por todos e todos por um é uma grande motivação, mas raramente faz com que uma linha de código seja escrita. Todo projeto de software começa com uma comichão que o desenvolvedor precisa coçar. Neste caso, a comichão começou com um projeto de intranet que a Optaros™ fez para um cliente para fornecer uma integração entre o Django®, uma estrutura de desenvolvimento de aplicativos da Web baseados em Python, e o Alfresco, uma plataforma de gerenciamento de conteúdo de software livre. Desenvolvida quando o CMIS era apenas uma idéia na OASIS, a integração apoia-se em Alfresco Web Scripts (uma estrutura para transferir sua própria API RESTful para Alfresco) no lado do servidor para transferir XML sobre HTTP entre a camada de apresentação baseada em Django e o repositório do Alfresco. Funciona muito bem, mas é específica do Alfresco. A refatoração da integração do Django para aproveitar o CMIS parecia uma boa idéia. Mas, melhor do que torná-la específica do Django, escolhemos primeiro lançar a cmislib como uma API para Python em um nível inferior. O benefício é que, além do Django, outros projetos Python, como Zope® e Plone®, além de aplicativos Python personalizados, podem se integrar mais facilmente com repositórios CMIS aproveitando a cmislib.

O último motivo egoísta é a produtividade do desenvolvedor. A maioria das empresas não tem o luxo de lidar com apenas um único repositório. E as soluções geralmente não podem antecipar o repositório específico que estará na ação ou, pelo menos, precisam da opção de alternar em algum ponto no caminho. O padrão CMIS obviamente ajuda nesses problemas, mas para que o trabalho seja realmente feito de maneira produtiva, são necessárias bibliotecas no lado do cliente. Outros projetos já estão em desenvolvimento para fornecer bibliotecas do cliente com base em Java™ e PHP para CMIS. Mas o Python é também bastante prevalecente na camada de apresentação, por isso é importante uma biblioteca do cliente com base em Python para o CMIS.

O que a API faz

O objetivo da cmislib é abstrair os detalhes de implementação subjacentes do CMIS. Os desenvolvedores não desejam ou não precisam aprender como o CMIS funciona para construir soluções sobre os repositórios CMIS. Em vez disso, a cmislib fornece um modelo de objetos fácil de desenvolver que será instantaneamente familiar a quem trabalha com um repositório de conteúdo ou lê as especificações do CMIS. Em vez de coleções, entradas e alimentações, os desenvolvedores trabalham com conceitos naturais de gerenciamento de conteúdo, como repositórios, pastas, documentos e ACLs.

Como mencionado, a cmislib usa a ligação RESTful AtomPub para se comunicar com servidores CMIS. Uma consideração de desenvolvimento importante era garantir que a cmislib não tivesse conhecimento específico de fornecedor sobre os repositórios de backend que está acessando—a biblioteca trata um provedor de CMIS como uma caixa preta. Ao usar a cmislib para se conectar a um provedor de CMIS, você dá o ponto de entrada do provedor de CMIS ou URL de serviço e algumas credenciais. A biblioteca descobre como interagir mais profundamente com o servidor interrogando as respostas do servidor.

Por exemplo, suponha que deseje obter uma lista de documentos que estejam atualmente retirados. A especificação CMIS indica que:

  • Uma coleção de documentos retirados existe.
  • repositoryId é um argumento necessário ao invocar o serviço getCheckedOutDocs.
  • Vários argumentos opcionais podem ser passados, a maioria deles relacionados à paginação do conjunto de resultados.
  • A resposta do serviço será uma alimentação Atom.

O que a especificação não indica, porém, é a URL exata para recuperar a coleção. Isso é deixado para o fornecedor. Uma das coisas que a cmislib trata é descobrir qual é a URL e como converter a resposta em objetos Python que podem ser trabalhados de acordo com a maneira do Python. A Listagem 1 mostra como essa interação parece a partir do shell do Python:

Listagem 1. Listando os documentos retirados em um repositório
>>> rs = repo.getCheckedOutDocs()

>>> len(rs)
2
>>> for doc in rs:
...     doc.title
... 
u'Response XML (Working Copy)'
u'd1 (Working Copy)'

De modo semelhante, suponha que você deseje fazer uma retirada de um documento. A especificação indica que, para realizar uma retirada, é necessário publicar uma entrada Atom na coleção retirada e o repositório retornará uma entrada Atom representando a Private Working Copy (PWC) do objeto que você acabou de retirar. Se você usar a cmislib, não precisará se preocupar em determinar a coleção, construir e publicar o XML da entrada Atom ou tratar da resposta XML. Em vez disso, basta chamar o método checkout no objeto e a cmislib devolve um objeto Document representando o PWC. A Listagem 2 mostra essa interação:

Listagem 2. Retirando um documento
>>> doc.title
u'testdoc'
>>> pwc = doc.checkout()
>>> pwc.title
u'testdoc (Working Copy)'
>>> doc.checkedOut
True

Abordagem de desenvolvimento e teste

Era importante que a cmislib fosse escrita o mais próximo possível da especificação. Assim, a primeira etapa era fazer stub das classes e dos métodos com o Eclipse e a especificação aberta de ponta a ponta. Acrescentei referências cruzadas para a especificação para comentários em linha, desse modo encontrei rapidamente o ponto aplicável na especificação mais tarde quando voltei para implementar o método.

Configurei meu processo de construção, documentação e controle do código de origem diretamente no início. Era importante ter isso estabelecido prematuramente, de forma que desenvolvedores adicionais pudessem participar do projeto e se aprofundarem rapidamente.

O código evoluiu de maneira iterativa. Cada iteração começava escrevendo testes de unidade para a nova funcionalidade e continuava com a codificação de fato dos métodos, até que os testes de unidade fossem aprovados. Comecei com o básico, como consultar as capacidades do repositório e recuperar objetos e propriedades dos objetos para validar a abordagem geral. Dali, avancei para escrever operações de maneira completa, retirada/devolução, relacionamentos e ACLs.

Os testes eram, inicialmente, um tanto difíceis, porque não existia nenhuma implementação de referência (a ligação AtomPub na implementação de referência do Apache Chemistry era somente leitura na época) e porque os fornecedores ainda estavam trabalhando em suas implementações. O Alfresco, um pioneiro do CMIS, tinha a implementação mais madura, por isso comecei com ele. Assim que a maioria dos testes de unidade estava terminando de maneira limpa com relação à Alfresco, comecei a testar com relação a fornecedores adicionais que tinham implementações de CMIS publicamente disponíveis. A IBM cortesmente ofereceu-se como voluntária para tornar sua implementação disponível. Adicionar essa segunda implementação foi esclarecedor, um grande exercício para cmislib e todos os fornecedores envolvidos. Localizamos problemas no lado do cliente e do servidor que poderiam não ter sido descobertos tão rapidamente sem esse tipo de teste de interoperabilidade.

Se você estiver desenvolvendo ferramentas CMIS ou APIs, como a cmislib, é crítico testar com relação ao maior número possível de servidores diferentes. A especificação é nova em folha e as implementações dos fornecedores ainda estão amadurecendo, mesmo daqueles que declaram conformidade total com a especificação de esboço. Os problemas mais comuns descobertos caíram em três grupos:

  • Implementações incompletas. O CMIS ainda é bastante novo. É comum localizar serviços ausentes (ACL, relacionamentos, políticas e logs de mudanças parecem ser os menos suportados no momento), recursos obrigatórios que não são suportados ainda (por exemplo, coleções obrigatórias e links que não são fornecidos) e valores codificados de forma permanente.
  • Interpretações divergentes da especificação. A especificação do CMIS é um documento bem escrito e fácil de ler, mas algumas coisas ainda têm interpretação imprecisa. Por exemplo, antes do esboço 6, o conteúdo da coleção retirada era vago. Deveria conter Private Working Copies (PWCs) dos objetos retirados ou os próprios objetos? Diferentes fornecedores interpretaram de forma diferente e implementavam de acordo com sua interpretação. Esse problema específico foi, desde então, esclarecido (PWCs é a resposta, se estiver curioso), mas é possível ver como isso poderia tornar difícil escrever um cliente interoperável.
  • Suposições incorretas. Às vezes, uma extensão específica do fornecedor para a especificação é óbvia e às vezes não. Se você codificar sua API com relação a um servidor e tratá-la como se fosse a implementação de referência, fez uma suposição de que outras implementações irão operar da mesma maneira. O desafio neste momento é que não existe uma implementação de referência do CMIS com uma ligação AtomPub que seja totalmente completa funcionalmente e 100% compatível com a especificação.

Alguns exemplos

O próximo artigo nesta série mostrará uma aplicação real da biblioteca percorrendo um script Python que pode ser usado para carregamento em massa de um repositório CMIS com ativos digitais e metadados. Os exemplos básicos a seguir foram retirados da documentação da cmislib e mostram como executar operações comuns com a API do shell interativo do Python. As operações incluem obter informações sobre o repositório, trabalhar com Pastas e Documentos e localizar objetos por consulta ao CMIS, caminho, ID do objeto ou relacionamento.

Obtendo um objeto do repositório

Os objetos CmisClient e Repository são pontos iniciais comuns para praticamente tudo que você desejar fazer com o repositório CMIS. Obter uma instância é simples—tudo que é necessário saber é o URL de serviço e as credenciais do repositório. É assim que funciona:

  1. Na linha de comando, inicie o shell do Python digitando python e pressionando Enter.
  2. Importe o CmisClient:
    >>> from cmislib.model import CmisClient
  3. Aponte o CmisClient para o URL de serviço do repositório:
    >>> client = CmisClient('http://cmis.alfresco.com/s/cmis', 'admin', 'admin')
  4. Os repositórios têm um ID—se você souber qual é, também pode obter o repositório pelo ID. Nesse caso, pediremos ao cliente o repositório padrão.
    >>> repo = client.defaultRepository
    >>> repo.id u'83beb297-a6fa-4ac5-844b-98c871c0eea9'
  5. Agora é possível obter as propriedades do repositório. Esse for-loop revela tudo que a cmislib sabe sobre o repositório. (Trunquei a listagem por questão de brevidade).
    >>> repo.name u'Main Repository'
    >>> info = repo.info
    >>> for k,v in info.items():
        ...     print "%s:%s" % (k,v)
        ...     
        cmisSpecificationTitle:Version 1.0 Committee Draft 04
        cmisVersionSupported:1.0
        repositoryDescription:None
        productVersion:3.2.0 (r2 2440)
        rootFolderId:workspace://SpacesStore/aa1ecedf-9551-49c5-831a-0502bb43f348
        repositoryId:83beb297-a6fa-4ac5-844b-98c871c0eea9
        repositoryName:Main Repository
        vendorName:Alfresco
        productName:Alfresco Repository (Community)

Trabalhando com Pastas e Documentos

Quando tiver o objeto Repository, poderá começar a trabalhar com objetos do repositório, como Pastas e Documentos.

  1. Crie uma nova pasta na raiz. Se estiver realmente acompanhando, nomeie sua pasta de maneira exclusiva se for testar em um repositório público.
    >>> root = repo.rootFolder
    >>> someFolder = root.createFolder('someFolder')
    >>> someFolder.id 
    u'workspace://SpacesStore/91f344ef-84e7-43d8-b379-959c0be7e8fc'
  2. Em seguida, poderá criar algum conteúdo:
    >>> someFile = open('test.txt', 'r')
    >>> someDoc = someFolder.createDocument('Test Document', contentFile=someFile)
  3. E, se desejar, poderá fazer dump das propriedades do documento recém-criado (essa é uma listagem parcial):
    >>> props = someDoc.properties
    >>> for k,v in props.items():
    ...     print '%s:%s' % (k,v)
    ...
    cmis:contentStreamMimeType:text/plain
    cmis:creationDate:2009-12-18T10:59:26.667-06:00
    cmis:baseTypeId:cmis:document
    cmis:isLatestMajorVersion:false
    cmis:isImmutable:false
    cmis:isMajorVersion:false
    cmis:objectId:workspace://SpacesStore/2cf36ad5-92b0-4731-94a4-9f3fef25b479

Recuperando objetos

Um objeto pode ser obtido de várias maneiras diferentes:

  • É possível executar uma consulta CMIS.
  • É possível pedir ao repositório para fornecer uma resposta para um caminho ou ID de objeto específico.
  • É possível passar através do repositório usando os filhos e/ou descendentes de uma pasta.
  • É possível obter os objetos de origem e de destino vinculados por um relacionamento.

Aqui estão alguns exemplos que mostram essas opções em operação:

  1. Localize o documento criado na seção anterior com uma pesquisa de texto completo.
    >>> results = repo.query("select * from cmis:document where contains('test')")
    >>> for result in results:
    ...     print result.name
    ...
    Test Document2
    Example test script.js
  2. Alternativamente, também pode obter objetos pelo seu caminho, desta maneira:
    >>> someDoc = repo.getObjectByPath('/someFolder/Test Document')
    >>> someDoc.id 
    u'workspace://SpacesStore/2cf36ad5-92b0-4731-94a4-9f3fef25b479'
  3. Ou pode recuperá-lo usando o ID do objeto, desta maneira:
    >>> someDoc = repo.getObject('workspace://SpacesStore/2cf36ad5-94a4-9f3fef25b479')
    >>> someDoc.name 
    u'Test Document'
  4. Os objetos de pasta têm métodos getChildren() e getDescendants() que retornarão um conjunto de resultados paginável:
    >>> children= someFolder.getChildren()
    >>> for child in children:
    ...     print child.name
    ...
    Test Document
    Test Document2
  5. Pastas e Documentos têm um método getRelationships() que retorna um conjunto de objetos de relacionamento. Os objetos Relationship podem dar o objeto de origem e de destino de cada extremidade do relacionamento.
    >>> rel = testDoc.getRelationships(includeSubRelationshipTypes='true')[0]
    >>> rel.source.name
    'testDoc1'
    >>> rel.target.name
    'testDoc2'
    >>> rel.properties['cmis:objectTypeId']
    'R:sc:relatedDocuments'

Você verá como trabalhar com outras partes da API, incluindo a capacidade de recuperar definições do tipo do objeto, no próximo artigo da série.


Conclusão

Este artigo forneceu uma breve visão geral da cmislib, como surgiu, o que faz e alguns exemplos básicos. Possivelmente, agora você está inspirado para verificar o CMIS mais profundamente. Se estiver interessado em Python, verifique a cmislib. Para obter um link, consulte Recursos. Se não estiver, explore algumas das outras ferramentas e bibliotecas de cliente que existem por aí. Por último, a comunidade CMIS precisa de você. É possível ajudar de muitas maneiras:

  • Se não existir uma biblioteca do cliente escrita no seu idioma favorito, crie uma como um projeto de software livre.
  • Ajude seu fornecedor de repositório a testar a implementação do CMIS.
  • Escreva integrações para o seu portal favorito ou estrutura de apresentação para permitir que eles trabalhem facilmente com repositórios CMIS.
  • Participe de projetos CMIS de software livre, como cmislib e Apache Chemistry.
  • Participe do esforço de especificação do CMIS envolvendo-se com o Comitê Técnico OASIS.

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=481934
ArticleTitle=Uma biblioteca de APIs do CMIS para Python, Parte 1: Apresentando o cmislib
publish-date=04092010