/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.mail.internal.thread.context;
import java.net.URL;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.xwiki.component.annotation.Component;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.web.XWikiRequest;
import com.xpn.xwiki.web.XWikiResponse;
import com.xpn.xwiki.web.XWikiServletResponseStub;
import com.xpn.xwiki.web.XWikiURLFactory;
/**
* Additionally to using {@link XWikiContext#clone()}, this implementation also tries to make sure that the cloned
* context is usable in a different thread. To do this, the request and the response are stubbed, the URL factory is
* reinitialized and the store session/transaction are cleared. This latter point is sensible since there is currently
* no way to clear the session/transaction only in the copied context. So the session/transaction is cleared in the
* original context before cloning, causing a side effect on the original context. You should never copy a context
* while a create/update transaction is in progress, since some changes would get rollbacked.
* <p>
* Note: The clone is still rather shallow, since many fields will still be shared with the original
* {@link XWikiContext}.
*
* @version $Id: fbb5f2d90c586d8af53162d9d1528493b229f9f2 $
* @since 7.1M2
*/
@Component
@Singleton
public class XWikiContextCopier implements Copier<XWikiContext>
{
@Inject
private Copier<XWikiRequest> xwikiRequestCloner;
/**
* {@inheritDoc}
*
* Any in progress session/transaction on the store retained by the original context will be closed/rollbacked
* prior cloning (see {@link com.xpn.xwiki.store.XWikiStoreInterface#cleanUp(XWikiContext)}). Therefore,
* the copy operation has a side effect on the original context. You should never copy a context
* while a create/update transaction is in progress, since some changes would get rollbacked.
*/
@Override
public XWikiContext copy(XWikiContext originalXWikiContext)
{
// Clean up the store session/transaction before cloning. For the hibernate store, in progress
// session/transaction is stored in the context, and would be swallow copied when the context is cloned.
// Cleaning after clone would not help, since it would close/rollback the session/transaction still referenced
// in the original context as well, causing this context to be corrupted.
// The correct way would be to not shallow clone the session/transaction when cloning the original context,
// since session/transaction are lazy initialize on request when missing.
originalXWikiContext.getWiki().getStore().cleanUp(originalXWikiContext);
// This is still a shallow clone, but at least for stuff like wikiID and userReference it gets the job done.
XWikiContext clonedXWikiContext = originalXWikiContext.clone();
// lets now build the stub context
// Copy the request from the context.
clonedXWikiContext.setRequest(this.xwikiRequestCloner.copy(originalXWikiContext.getRequest()));
// Force forged context response to a stub response, since the current context response
// will not mean anything anymore when running in the scheduler's thread, and can cause
// errors.
XWikiResponse stub = new XWikiServletResponseStub();
clonedXWikiContext.setResponse(stub);
// feed the dummy context
if (clonedXWikiContext.getURL() == null) {
try {
clonedXWikiContext.setURL(new URL("http://www.mystuburl.com/"));
} catch (Exception e) {
// the URL is clearly well formed
}
}
XWikiURLFactory xurf =
originalXWikiContext.getWiki().getURLFactoryService()
.createURLFactory(originalXWikiContext.getMode(), originalXWikiContext);
clonedXWikiContext.setURLFactory(xurf);
return clonedXWikiContext;
}
}