Implementando SCORM: Construindo a API

September 23rd, 2003 § 1 comment

Continuando a minha série de entradas sobre o SCORM, este artigo discute alguns aspectos específicos da implementação da própria API, tanto no lado cliente como no lado servidor, focando também na resolução de alguns dificuldades encontradas.

Como eu mencionei nos textos anteriores, o padrão ainda apresenta uma grande imaturidade em alguns aspectos. A própria API, embora possua uma interface bem estabelecida, tem algumas falhas que tornam uma implementação rigorosa um trabalho um tanto ou quanto complicado.

Dos problemas causados por estas falhas, os maiores que eu encontrei na implementação foram três: o modelo de segurança do JavaScript, a criação de um cliente HTTP JavaScript que funcionasse nos vários navegadores que eu precisava suportar, e o gerenciamento da sessão de execução da API. O primeiro problema se refere ao fato de que um determinado código JavaScript só pode chamar uma URL que esteja no mesmo domínio em que o mesmo está executando. Na verdade, para sermos justos, esse não é bem um problema do JavaScript. Essa limitação existe para aumentar a segurança do código rodando em páginas Web e, se não existisse, teríamos muito mais problemas com o uso indevido do JavaScript. E, de mais a mais, o modelo de segurança do JavaScript segue de perto os outros modelos implementados por um navegador seja para Java ou a própria comunicação entre diferentes frames em uma página.

Ainda assim, em alguns casos essa limitação pode ser tornar um impedimento para o uso da API. Nesse caso não há uma outra solução possível a não ser exigir que um curso invoque a API a partir de um mesmo domínio. Quaisquer outras soluções exigiriam baixar o nível de segurança do usuário ou pedir que ele executasse configurações extras — coisas que não são de nenhum modo interessantes para a facilidade de uso de um sistema.

O segundo problema é conseguir que, a partir do JavaScript, dados possam ser recebidos de um URL genérica. Isso é necessário para que a implementação cliente da API posso comunicar-se com sua parte correspondente no servidor, como é o caso da maioria das implementações. Um invocação da API SCORM normalmente existe em duas etapas de ida e volta. O curso invoca a API local (na verdade, um envoltório que expõe as funções SCORM para o curso) e então a API local invoca a sua parte correspondente no servidor por algum mecanismo RPC, obtendo e retornando os dados necessários.

Essa divisão vem do fato de que o curso deve ser comunicar com uma API genérica e de que essa API precisa ser implementada em algo acessível ao curso por meio da JavaScript. Não é possível, pelo SCORM, usar os mecanismos normais de submissão de formulários ao servidor, já que um curso SCORM deve ser o mais genérico possível e fazer uso somente de recursos comuns a todas plataformas. Isso força a divisão da API nessas duas partes integradas: o envoltório local, que faz a interface entre o curso e o servidor por meio de um mecanismo de transporte suportado pelo JavaScript; e um serviço Web (não necessariamente SOAP), que recebe pedidos do envoltório local, os executa no contexto apropriado com os dados que dispõe, e retorna os dados resultantes para que o evoltório local os passe ao curso.

Infelizmente, não existe um mecanismo que permita construir a parte de transporte em todos os navegadores em uso hoje de uma maneira comum, ou seja, solicitar uma URL qualquer para envio e recebimento de dados. Mesmo uma applet Java, que seria considerada a opção mais simples, exigiria a instação de um plugin de 5MB em dois dos navegadores mais comuns de hoje: Internet Explorer 6 no Windows XP e qualquer navegador do projeto Mozilla.

A solução que eu usei foi criar um envelope para a API que determina qual mecanismo de transporte usar dependendo do navegador, e, em alguns casos, automaticamente seleciona mecanismos alternativos dependendo das condições. Assim, o curso invoca o envelope através das chamadas descritas da especificação e este envelope seleciona o melhor mecanismo de transporte.

Os mecanismos que eu usei foram: XML no Internet Explorer 5+; XML no Mozilla; Java tanto no Internet Explorer quanto no Mozilla, caso não seja possível usar XML; ActiveX no Internet Explorer caso não seja possível usar Java ou XML; e Java nos demais navegadores. A implementação testa primeiramente o mecanismo mais fácil e rápido. Caso não seja possível usá-lo, ela cria as condições para o uso do próximo e tenta usá-lo então. Isso é repetido até que ela consiga um mecanismo ou retorne uma falha. Para o curso, isso é transparente.

Essa solução funcionou de maneira simples e a implementação ficou resumida a umas poucas centenas de linhas de JavaScript, das quais a maior parte é o código de deteção de suporte e o código que passa as requisições entres os objetos. As próprias requisições tomaram alguns dezenas de linhas.

No lado do servidor, a API é uma página dinâmica comum, implementada em uma linguagem qualquer suportada pelo servidor, que envia os dados no formato apropriado. O padrão é XML, mas caso o transporte cliente não o suporte diretamente, como é o caso de Java, que, na maioria das máquinas virtuais dos navegadores em uso atualmente, não possui o suporte implícito, o formato retornado é CSV.

O terceiro problema foi o mais simples de resolver. Embora o SCORM não especifique isso de maneira clara, o fato é que cada curso estabele sua própria sessão por usuário. Essa sessão precisa terminar claramente para que erros não sejam retornados nas próximas invocações da API. Entretanto, pela própria natureza da tecnologia usada, a função da API que finaliza a sessão pode não vir a ser chamada, como, por exemplo, no caso do navegador falhar em invocá-la por ter fechado incorretamente.

Para evitar quaisquer problemas nessa área, a solução que eu encontrei foi passar um número randômico para a API no servidor que é escolhido e enviado pela API local. Esse número é gerado a cada chamada da função que inicializa a sessão. A API no servidor, usa esse número para detectar falhas no término ou na inicialização. Caso o número recebido seja diferente, ela processa as funções da maneira usual, sem retornar erros, entendendo então, que o sessão anterior não terminou de maneira apropriada.

Estes foram os problemas tecnológicos principais que eu encontrei na implementação da API. Os demais problemas vieram mais da tentativa de entender as contradições existentes na especificação do que de resolver falhas tecnológicas.

Um detalhe importante a lembrar na hora da implementação é que a API local também é parte do LMS, mesmo executando no cliente e pode, portanto, fazer uso de informações que este possui, seja pelo manifesto do curso ou da própria base de dados do LMS no momento de sua inicialização. Inclusive, esse é o único modo que ela tem para enviar as informações necessárias a sua parte correspondente do servidor. A única obrigação da API local quanto ao curso é fornecer a interface pedida pela especificação SCORM.

A implementação da API no servidor não é difícil mas requer um cuidado especial na sincronização dos dados com o banco de dados e na manutenção do estado de sessão das invocações feita pela API local. Um exemplo do primeiro caso é quando um módulo do curso informa o elemento “cmi.core.score.raw”. A API no servidor deve, ao mesmo tempo em que salva o valor, compará-lo ao valor de crédito do módulo, se este existir, para verificar se o usuário passou ou falhou no mesmo. Um exemplo de segundo caso são os elementos “cmi.core.sessiontime” e “cmi.core.totaltime” que devem ser coordenados pela API. Essa função é melhor realizada pela API no servidor, que deve acumular os tempos de sessão entre as mesmas.

A maneira mais fácil de implementar a API no servidor é criar um tabela no banco de dados no LMS que sirva com um dicionário para os elementos que a implementação suporta. Essa tabela pode especificar quaisquer dados necessários que ajudem a API a decidir com lidar com um elemento usando campos que especifiquem essas informações extras. Alguns exemplos dessas informação são se o mesmo é somente-leitura ou somente-escrita, se a API lidará com ele totalmente em tempo de execução ou gravará e lerá valores, etc.

Além dos elementos da especificação eu criei também alguns elementos extras, que são extensões usadas por minha própria implementações. Por exemplo, um elemento “ext.api.sessionid” especifica o número da sessão que eu expliquei anteriormente. Nenhum curso usuará este elemento mas a API se serve do mesmo para controlar a comunicação de maneira mais eficiente. Outros elementos que eu use foram “ext.api.state”, “ext.api.lasterror” e “ext.api.last_diagnostic”. Obviamente, eu não usei o prefixo “ext”, e sim um prefixo que indica a empresa para a qual eu trabalho atualmente.

Da mesma forma que a API local, a API no lado servidor, mesmo usando uma linguagem como o ASP, que não é tão modularizada, também não precisou mais do que umas poucas centenas de linhas para ser implementada. Em outra linguagem mais sofisticada, esse número provavelmente seria grandemente reduzido. Porém, esse número se refere somente à implementação dos elementos mandatórios da especificação.

Concluindo, a implementação da API é trabalhosa e deve ser feita com cuidado, para evitar problemas futuros, mas também não é uma tarefa hérculea. Os mais complicado é interpretar as informações descritas na especificação e traduzi-las para o código necessário. Em alguns casos, essas informações são muito ambíguas e o próprio programador deve decidir qual caminho vai adotar.

Espero que esse artigo tenha ajudado. Nos próximos artigos, outros problemas semelhantes da implementação serão abordados.

§ One Response to Implementando SCORM: Construindo a API

What's this?

You are currently reading Implementando SCORM: Construindo a API at Superfície Reflexiva.

meta