/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* 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 org.seasar.mayaa.impl.engine.specification;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.mayaa.builder.SpecificationBuilder;
import org.seasar.mayaa.cycle.scope.ApplicationScope;
import org.seasar.mayaa.engine.specification.NodeTreeWalker;
import org.seasar.mayaa.engine.specification.Specification;
import org.seasar.mayaa.engine.specification.SpecificationNode;
import org.seasar.mayaa.engine.specification.serialize.NodeReferenceResolver;
import org.seasar.mayaa.engine.specification.serialize.NodeResolveListener;
import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.ParameterAwareImpl;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.cycle.DefaultCycleLocalInstantiator;
import org.seasar.mayaa.impl.engine.EngineUtil;
import org.seasar.mayaa.impl.engine.specification.serialize.NodeSerializeController;
import org.seasar.mayaa.impl.engine.specification.serialize.SerializeThreadManager;
import org.seasar.mayaa.impl.provider.ProviderUtil;
import org.seasar.mayaa.impl.source.NullSourceDescriptor;
import org.seasar.mayaa.impl.util.ObjectUtil;
import org.seasar.mayaa.source.SourceDescriptor;
/**
* @author Masataka Kurihara (Gluegent, Inc.)
*/
public class SpecificationImpl extends ParameterAwareImpl
implements Specification, NodeReferenceResolver, CONST_IMPL {
private static final long serialVersionUID = -7503898036935182468L;
private static final Log LOG =
LogFactory.getLog(SpecificationImpl.class);
private Date _buildTimestamp;
private Date _builtSourceTime;
private SourceDescriptor _source;
private volatile NodeTreeWalkerImpl _delegateNodeTreeWalker;
private boolean _hasSource;
private boolean _deprecated = true;
private int _lastSequenceID;
private transient boolean _specificationSerialize;
protected boolean needCheckTimestamp() {
return EngineUtil.getEngineSettingBoolean(CHECK_TIMESTAMP, true);
}
protected boolean isSourceExists() {
return getSource().exists();
}
public boolean isSpecificationSerialize() {
return _specificationSerialize && !IS_SECURE_WEB;
}
public void setSpecificationSerialize(boolean specificationSerialize) {
_specificationSerialize = specificationSerialize;
}
protected SpecificationBuilder getBuilder() {
return ProviderUtil.getSpecificationBuilder();
}
protected void setTimestamp(Date buildTimestamp) {
_buildTimestamp = buildTimestamp;
}
protected NodeTreeWalker getNodeTreeWalker() {
if (_delegateNodeTreeWalker == null) {
synchronized (this) {
if (_delegateNodeTreeWalker == null) {
_delegateNodeTreeWalker = new NodeTreeWalkerImpl();
_delegateNodeTreeWalker.setOwner(this);
}
}
}
return _delegateNodeTreeWalker;
}
// デバッグのときだけ有効にすること。finalize()をオーバーライドするとFinalizerなどから特別扱いされる。
// protected void finalize() throws Throwable {
// if (LOG.isTraceEnabled()) {
// LOG.trace(toString() + " unloaded.");
// }
// super.finalize();
// }
public String toString() {
String className = ObjectUtil.getSimpleClassName(getClass());
return getSystemID() + "(" + className + "@" + Integer.toHexString(hashCode()) + ")";
}
// Specification implements ------------------------------------
// TODO isDeprecatedの高速化
public boolean isDeprecated() {
if (_deprecated == false) {
_deprecated = _hasSource != isSourceExists();
if (_deprecated == false) {
if (_hasSource == false) {
return false;
}
_deprecated = (getTimestamp() == null);
if (_deprecated == false) {
if (needCheckTimestamp() == false) {
return false;
}
Date sourceTime = getSource().getTimestamp();
// リバートしてもビルドする。
_deprecated =
sourceTime.equals(_builtSourceTime) == false;
}
}
}
return _deprecated;
}
public void build() {
build(true);
}
public void build(boolean rebuild) {
if (isDeprecated()) {
setTimestamp(new Date());
_hasSource = isSourceExists();
if (_hasSource) {
LOG.debug(getSystemID() + " build start.");
Date sourceTime = getSource().getTimestamp();
_lastSequenceID = 0;
getBuilder().build(this);
_builtSourceTime = sourceTime;
_deprecated = false;
if (isSpecificationSerialize()) {
SerializeThreadManager.serializeReserve(
this, CycleUtil.getServiceCycle().getApplicationScope().getUnderlyingContext());
}
return;
}
// rebuildの場合は存在したソースが無くなったことを意味するため、タイムスタンプを0にしない
if (rebuild == false) {
setTimestamp(new Date(0));
}
_builtSourceTime = new Date(0);
_deprecated = false;
}
}
public Date getTimestamp() {
return _buildTimestamp;
}
public void setSource(SourceDescriptor source) {
_source = source;
}
public SourceDescriptor getSource() {
if (_source == null) {
_source = new NullSourceDescriptor();
}
return _source;
}
// SequenceIDGenerator implements ------------------------------------
public void resetSequenceID(int sequenceID) {
_lastSequenceID = sequenceID;
}
public int nextSequenceID() {
return _lastSequenceID++;
}
// NodeTreeWalker implements ------------------------------------
public void clearChildNodes() {
if (_delegateNodeTreeWalker != null) {
_delegateNodeTreeWalker.clearChildNodes();
}
}
public void setParentNode(NodeTreeWalker parentNode) {
throw new IllegalStateException();
}
public NodeTreeWalker getParentNode() {
return null;
}
public void addChildNode(NodeTreeWalker childNode) {
getNodeTreeWalker().addChildNode(childNode);
}
public void insertChildNode(int index, NodeTreeWalker childNode) {
getNodeTreeWalker().insertChildNode(index, childNode);
}
public Iterator iterateChildNode() {
return getNodeTreeWalker().iterateChildNode();
}
public boolean removeChildNode(NodeTreeWalker node) {
return getNodeTreeWalker().removeChildNode(node);
}
public NodeTreeWalker getChildNode(int index) {
return getNodeTreeWalker().getChildNode(index);
}
public int getChildNodeSize() {
return getNodeTreeWalker().getChildNodeSize();
}
public void kill() {
// TODO deprecated のため削除
}
// NodeReferenceResolverFinder implements --------------------------------------
public NodeReferenceResolver findNodeResolver() {
return getNodeTreeWalker().findNodeResolver();
}
// PositionAware overrides ------------------------------------
public String getSystemID() {
if (getSource() == null) {
return null;
}
return getSource().getSystemID();
}
public int getLineNumber() {
return 0;
}
public boolean isOnTemplate() {
return false;
}
// for serialize
private static final String SERIALIZE_CONTROLLER_KEY =
SpecificationImpl.class.getName() + "#serializeController";
static {
CycleUtil.registVariableFactory(
SERIALIZE_CONTROLLER_KEY,
new DefaultCycleLocalInstantiator() {
public Object create(Object[] params) {
return new NodeSerializeController();
}
public void destroy(Object instance) {
if (instance instanceof NodeSerializeController) {
((NodeSerializeController) instance).release();
}
}
});
}
protected static File getSerializedFile(String systemID) {
ApplicationScope scope =
CycleUtil.getServiceCycle().getApplicationScope();
String cachePath = scope.getRealPath("WEB-INF/.mayaaSpecCache");
File cacheDir = new File(cachePath);
cacheDir.mkdirs();
return new File(cacheDir,
systemID.substring("/".length()).replace('/', '`') + ".ser");
}
public void serialize() {
synchronized(this) {
try {
File outputFile = getSerializedFile(getSystemID());
ObjectOutputStream stream =
new ObjectOutputStream(new FileOutputStream(outputFile));
try {
nodeSerializer().init();
try {
stream.writeObject(this);
} finally {
nodeSerializer().release();
}
} finally {
stream.close();
}
} catch (IOException e) {
LOG.error("page serialize failed.", e);
}
}
}
protected void afterDeserialize() {
// no-op
}
public Specification deserialize(String systemID) {
synchronized(this) {
File cacheFile = getSerializedFile(systemID);
if (cacheFile.exists() == false) {
return null;
}
Specification result;
try {
ObjectInputStream stream =
new ObjectInputStream(new FileInputStream(cacheFile));
try {
nodeSerializer().init();
try {
result = (Specification) stream.readObject();
if (result instanceof SpecificationImpl) {
((SpecificationImpl) result).afterDeserialize();
}
} finally {
nodeSerializer().release();
}
} finally {
stream.close();
}
return result;
} catch(Throwable e) {
String message =
getSystemID() + " specification deserialize failed.";
if (e.getMessage() != null) {
message += " " + e.getMessage();
}
LOG.info(message);
cacheFile.delete();
return null;
}
}
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (_delegateNodeTreeWalker != null) {
_delegateNodeTreeWalker.setOwner(this);
}
for (Iterator it = iterateChildNode(); it.hasNext(); ) {
NodeTreeWalker child = (NodeTreeWalker) it.next();
child.setParentNode(this);
}
nodeSerializer().specLoaded(this);
}
private void writeObject(ObjectOutputStream out)
throws IOException {
out.defaultWriteObject();
}
public static NodeSerializeController nodeSerializer() {
return (NodeSerializeController) CycleUtil.getGlobalVariable(
SERIALIZE_CONTROLLER_KEY, null);
}
// NodeReferenceResolver implements ----------------------------
public void registResolveNodeListener(
String uniqueID, NodeResolveListener listener) {
nodeSerializer().registResolveNodeListener(
uniqueID, listener);
}
public void nodeLoaded(SpecificationNode item) {
nodeSerializer().nodeLoaded(item);
}
}