blob: dd9145d35aee7a4d97c680ec54e031634aa520c3 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.mercury.spi.http.client.deploy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.maven.mercury.crypto.api.StreamObserver;
import org.apache.maven.mercury.crypto.api.StreamVerifier;
import org.apache.maven.mercury.logging.IMercuryLogger;
import org.apache.maven.mercury.logging.MercuryLoggerManager;
import org.apache.maven.mercury.spi.http.client.HttpClientException;
import org.apache.maven.mercury.spi.http.validate.Validator;
import org.apache.maven.mercury.transport.api.Binding;
import org.apache.maven.mercury.transport.api.Server;
import org.mortbay.jetty.HttpHeaders;
import org.mortbay.jetty.client.HttpClient;
public abstract class DeploymentTarget
{
private static final IMercuryLogger log = MercuryLoggerManager.getLogger(DeploymentTarget.class);
protected Server _server;
protected HttpClient _httpClient;
protected String _batchId;
protected Binding _binding;
protected Set<Validator> _validators;
protected TargetState _targetState;
protected TargetState _checksumState;
protected HttpClientException _exception;
protected String _remoteJettyUrl;
protected Set<StreamObserver> _observers = new HashSet<StreamObserver>();
protected List<StreamVerifier> _verifiers = new ArrayList<StreamVerifier>();
protected int _checkSumFilesDeployed = -1;
public abstract void onComplete();
public abstract void onError( HttpClientException exception );
public class TargetState
{
public static final int __START_STATE = 1;
public static final int __REQUESTED_STATE = 2;
public static final int __READY_STATE = 3;
private int _state;
private Exception _exception;
public TargetState()
{
_state = __START_STATE;
}
public synchronized void ready()
{
setState( __READY_STATE );
}
public synchronized void ready( Exception e )
{
setState( __READY_STATE );
_exception = e;
}
public synchronized void requested()
{
setState( __REQUESTED_STATE );
}
public synchronized boolean isStart()
{
return _state == __START_STATE;
}
public synchronized boolean isRequested()
{
return _state == __REQUESTED_STATE;
}
public synchronized boolean isError()
{
return _exception != null;
}
public boolean isReady()
{
return _state == __READY_STATE;
}
public synchronized void setState( int status )
{
_state = status;
}
public synchronized int getState()
{
return _state;
}
public synchronized Exception getException()
{
return _exception;
}
}
public DeploymentTarget( Server server, HttpClient client, String batchId, Binding binding, Set<Validator> validators, Set<StreamObserver> observers )
{
_server = server;
_httpClient = client;
_batchId = batchId;
_binding = binding;
_validators = validators;
for (StreamObserver o:observers)
{
if (StreamVerifier.class.isAssignableFrom(o.getClass()))
_verifiers.add((StreamVerifier)o);
_observers.add(o);
}
if ( _binding == null )
{
throw new IllegalArgumentException( "Nothing to deploy - null binding" );
}
if ( _binding.isFile() && (_binding.getLocalFile() == null || !_binding.getLocalFile().exists()) )
{
throw new IllegalArgumentException( "Nothing to deploy - local file not found: " + _binding.getLocalFile() );
}
if( _binding.isInMemory() && _binding.getLocalInputStream() == null )
{
throw new IllegalArgumentException( "Nothing to deploy - inMemory binding with null stream" );
}
_targetState = new TargetState();
_checksumState = new TargetState();
if (_verifiers.isEmpty())
_checksumState.ready();
}
public Binding getBinding()
{
return _binding;
}
public void deploy()
{
updateState( null );
}
private synchronized void updateState( Throwable t )
{
if (log.isDebugEnabled())
{
log.debug("updateState: exception="+t+" targetState="+_targetState.getState()+" checksumState="+_checksumState.getState()+" verifiers="+_verifiers.size()+" checksumsdeployed="+_checkSumFilesDeployed);
}
if ( t != null && _exception == null )
{
_exception = ( t instanceof HttpClientException ? (HttpClientException) t : new HttpClientException( _binding, t ) );
}
if (_exception != null)
{
onError(_exception);
}
else
{
//if the target file can be uploaded, then upload it, calculating checksums on the fly as necessary
if ( _targetState.isStart() )
{
deployLocalFile();
}
//Upload the checksums
else if ( _targetState.isReady() && (_verifiers.size()>0) && (_checkSumFilesDeployed < (_verifiers.size() -1)) )
{
deployNextChecksumFile();
}
else if ( _targetState.isReady() && (_checksumState.isReady()))
{
onComplete();
}
}
}
private void deployLocalFile()
{
FilePutExchange fileExchange = new FilePutExchange( _server, _batchId, _binding, _binding.getLocalFile(), _observers, _httpClient )
{
public void onFileComplete( String url, File localFile )
{
DeploymentTarget.this._remoteJettyUrl = getRemoteJettyUrl();
_targetState.ready();
updateState( null );
}
public void onFileError( String url, Exception e )
{
DeploymentTarget.this._remoteJettyUrl = getRemoteJettyUrl();
_targetState.ready( e );
updateState( e );
}
};
if( _server != null && _server.hasUserAgent() )
fileExchange.setRequestHeader( HttpHeaders.USER_AGENT, _server.getUserAgent() );
_targetState.requested();
fileExchange.send();
}
private void deployNextChecksumFile()
{
Binding binding = _binding;
File file = null;
StreamVerifier v = _verifiers.get(++_checkSumFilesDeployed);
//No local checksum file, so make a temporary one using the checksum we
//calculated as we uploaded the file
try
{
URL url = _binding.getRemoteResource();
if (url != null)
{
url = new URL( url.toString() + v.getAttributes().getExtension() );
}
String localFileName = getFileName(url);
file = File.createTempFile( localFileName, ".tmp" );
file.deleteOnExit();
OutputStreamWriter fw = new OutputStreamWriter( new FileOutputStream( file ), "UTF-8" );
fw.write( v.getSignature() );
fw.close();
binding = new Binding(url, file);
}
catch ( Exception e )
{
_checksumState.ready( e );
}
//upload the checksum file
Set<StreamObserver> emptySet = Collections.emptySet();
FilePutExchange checksumExchange = new FilePutExchange( _server, _batchId, binding, file, emptySet, _httpClient )
{
public void onFileComplete( String url, File localFile )
{
DeploymentTarget.this._remoteJettyUrl = getRemoteJettyUrl();
if (_checkSumFilesDeployed == (_verifiers.size() - 1))
_checksumState.ready();
updateState( null );
}
public void onFileError( String url, Exception e )
{
DeploymentTarget.this._remoteJettyUrl = getRemoteJettyUrl();
if (_checkSumFilesDeployed == (_verifiers.size() - 1))
_checksumState.ready( e );
updateState( e );
}
};
if( _server != null && _server.hasUserAgent() )
checksumExchange.setRequestHeader( HttpHeaders.USER_AGENT, _server.getUserAgent() );
_checksumState.requested();
checksumExchange.send();
}
public boolean isRemoteJetty()
{
return _remoteJettyUrl != null;
}
public String getRemoteJettyUrl()
{
return _remoteJettyUrl;
}
public synchronized boolean isComplete()
{
return ( _checksumState.isReady() && _targetState.isReady() );
}
public String getFileName (URL url)
{
if (url==null)
return "";
String localFileName = url.getFile();
int i = localFileName.indexOf('?');
if (i > 0)
localFileName = localFileName.substring(0, i);
if (localFileName.endsWith("/"))
localFileName = localFileName.substring(0, localFileName.length()-1);
i = localFileName.lastIndexOf('/');
if (i >= 0)
localFileName = localFileName.substring(i+1);
return localFileName;
}
public String toString()
{
return "DeploymentTarget:" + _binding.getRemoteResource() + ":" + _targetState + ":" + _checksumState + ":" + isComplete();
}
}