/**
* personium.io
* Copyright 2014 FUJITSU LIMITED
*
* Licensed 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 com.fujitsu.dc.engine.source;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.fujitsu.dc.common.es.EsType;
import com.fujitsu.dc.common.es.response.DcGetResponse;
import com.fujitsu.dc.core.model.file.BinaryDataAccessException;
import com.fujitsu.dc.core.model.file.BinaryDataAccessor;
import com.fujitsu.dc.engine.DcEngineException;
import com.fujitsu.dc.engine.EsModel;
import com.fujitsu.dc.engine.utils.DcEngineConfig;
/**
* サービスコレクションの情報からソースの情報を管理する.
*/
public class EsServiceResourceSourceManager implements ISourceManager {
/** ログオブジェクト. */
private static Logger log = LoggerFactory.getLogger(EsServiceResourceSourceManager.class);
/** ESインデックス. */
private String index;
/** ESタイプ. */
private String type;
/** ESID. */
private String id;
/** ESRoutingID. */
private String routingId;
/** ESアクセッサtype. */
private EsType typ;
/** ODataコレクションのPROPPATCH情報. */
private String serviceCollectionInfo;
/** ESから取得したODataコレクションの配下のソース情報. */
private Map<?, ?> sourceInfo;
/**
* コンストラクタ.
* @param index 対象サービスコレクションのESのインデックス
* @param type 対象サービスコレクションのESのタイプ
* @param id 対象サービスコレクションのESのID
* @param routingId 対象サービスコレクションのESのルーティングID
*/
public EsServiceResourceSourceManager(String index, String type, String id, String routingId) {
this.index = index;
this.type = type;
this.id = id;
this.routingId = routingId;
log.info("ElasticSearch index: [" + this.index + "] type: [" + this.type + "] "
+ "id: [" + this.id + "] routingId :[" + this.id + "]");
this.typ = EsModel.type(this.index, this.type, this.routingId, 0, 0);
}
/**
* サービスコレクションの情報を取得.
* @throws DcEngineException DcEngineException
*/
private void loadServiceCollectionInfo() throws DcEngineException {
if (this.sourceInfo != null) {
return;
}
// elasticsearchからPROPを取得する
// Type 名に # は使えないっぽい。
if (this.routingId == null) {
log.info("Routing ID is empty.");
throw new DcEngineException("404 Not Found (Request Header invalid) ",
DcEngineException.STATUSCODE_NOTFOUND);
}
// サービスコレクションを取得
DcGetResponse getResp = this.typ.get(this.id);
if (!getResp.isExists()) {
log.info("Service Collection id not found to ElasticSearch (" + this.id + ")");
throw new DcEngineException("404 Not Found (Service Collection invalid) ",
DcEngineException.STATUSCODE_NOTFOUND);
}
// スクリプトの情報を取得する
this.serviceCollectionInfo = (String) ((Map<?, ?>) getResp.getSource().get("d")).get("service@urn:x-dc1:xmlns");
if (null == this.serviceCollectionInfo) {
log.info("Service property Invalid ");
throw new DcEngineException("404 Not Found (Service property invalid) ",
DcEngineException.STATUSCODE_NOTFOUND);
}
log.debug("scriptPath: [" + this.serviceCollectionInfo + "] ");
// childrenを取る (__src)
String children = (String) ((Map<?, ?>) getResp.getSource().get("o")).get("__src");
// __src の情報を取得
getResp = this.typ.get(children);
if (!getResp.isExists()) {
log.info("Service Source Colleciton(__src) not found (" + children + ")");
throw new DcEngineException("404 Not Found (Service Source Collection invalid) ",
DcEngineException.STATUSCODE_NOTFOUND);
}
this.sourceInfo = (Map<?, ?>) getResp.getSource().get("o");
}
/**
* サービスコレクションに設定されたサービスサブジェクトの取得.
* @return サービスサブジェクト
* @throws DcEngineException DcEngineException
*/
public String getServiceSubject() throws DcEngineException {
this.loadServiceCollectionInfo();
// サービスサブジェクトの取得
return getServiceSubject(this.serviceCollectionInfo);
}
/**
* サービス名に対応したスクリプトファイル名の取得.
* @param servicePath サービス名
* @return スクリプトファイル名
* @throws DcEngineException DcEngineException
*/
public String getScriptNameForServicePath(String servicePath) throws DcEngineException {
this.loadServiceCollectionInfo();
return getScriptName(this.serviceCollectionInfo, servicePath);
}
/**
* ソースファイルを取得.
* @param sourceName ソースファイル名
* @return ソースファイルの中身
* @throws DcEngineException DcEngineException
*/
public String getSource(String sourceName) throws DcEngineException {
this.loadServiceCollectionInfo();
// 対象のスクリプトの情報を取得する
String sourceNodeId = (String) this.sourceInfo.get(sourceName);
if (sourceNodeId == null) {
log.info("Service Source not found (" + sourceName + ")");
throw new DcEngineException("404 Not Found", DcEngineException.STATUSCODE_NOTFOUND);
}
DcGetResponse getResp = this.typ.get(sourceNodeId);
if (!getResp.isExists()) {
log.info("Service Source not found (" + sourceName + ")");
throw new DcEngineException("404 Not Found", DcEngineException.STATUSCODE_NOTFOUND);
}
BinaryDataAccessor binaryAccessor = new BinaryDataAccessor(DcEngineConfig.getBlobStoreRoot(), this.index
.substring(DcEngineConfig.getUnitPrefix().length() + 1), DcEngineConfig.getFsyncEnabled());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
binaryAccessor.copy(sourceNodeId, baos);
return baos.toString("UTF-8");
} catch (BinaryDataAccessException e) {
log.info("UserScript Encoding error(UnsupportedEncodingException) ", e);
throw new DcEngineException("404 UserScript Encoding error", DcEngineException.STATUSCODE_NOTFOUND, e);
} catch (UnsupportedEncodingException e) {
log.info("UserScript Encoding error(UnsupportedEncodingException) ", e);
throw new DcEngineException("404 UserScript Encoding error", DcEngineException.STATUSCODE_NOTFOUND, e);
}
}
/**
* サービス名からスクリプトファイルのパスを取得する.
* @param xml XML文字列
* @param svcName サービス名
* @return スクリプトファイルパス
*/
private String getScriptName(final String xml, final String svcName) {
String scriptName = "";
DocumentBuilder builder = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
Document doc = null;
InputStream is = new ByteArrayInputStream(xml.getBytes());
try {
doc = builder.parse(is);
NodeList nl = doc.getElementsByTagNameNS("*", "path");
for (int i = 0; i < nl.getLength(); i++) {
NamedNodeMap nnm = nl.item(i).getAttributes();
if (nnm.getNamedItem("name").getNodeValue().equals(svcName)) {
scriptName = nnm.getNamedItem("src").getNodeValue();
}
}
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
return scriptName;
}
/**
* サービス設定からサービスサブジェクトの値を取得する.
* @param xml XML文字列
*/
private String getServiceSubject(final String xml) {
DocumentBuilder builder = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
builder = factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
}
Document doc = null;
InputStream is = new ByteArrayInputStream(xml.getBytes());
try {
doc = builder.parse(is);
Element el = doc.getDocumentElement();
return el.getAttribute("subject");
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}