--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.maven.archiva</groupId>
+ <artifactId>archiva-base</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>archiva-policies</artifactId>
+ <name>Archiva Base :: Policies</name>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven.archiva</groupId>
+ <artifactId>archiva-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-digest</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>1.2_Java1.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-slf4j-logging</artifactId>
+ <version>1.1-alpha-1-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.archiva.common.utils.VersionUtil;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.File;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * AbstractUpdatePolicy
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public abstract class AbstractUpdatePolicy
+ extends AbstractLogEnabled
+ implements PreDownloadPolicy
+{
+ /**
+ * The DISABLED policy means that the artifact retrieval isn't even attempted,
+ * let alone updated locally.
+ */
+ public static final String DISABLED = "disabled";
+
+ /**
+ * <p>
+ * The DAILY policy means that the artifact retrieval occurs only if one of
+ * the following conditions are met...
+ * </p>
+ * <ul>
+ * <li>The local artifact is not present.</li>
+ * <li>The local artifact has a last modified timestamp older than (now - 1 day).</li>
+ * </ul>
+ */
+ public static final String DAILY = "daily";
+
+ /**
+ * <p>
+ * The HOURLY policy means that the artifact retrieval occurs only if one of
+ * the following conditions are met...
+ * </p>
+ * <ul>
+ * <li>The local artifact is not present.</li>
+ * <li>The local artifact has a last modified timestamp older than (now - 1 hour).</li>
+ * </ul>
+ */
+ public static final String HOURLY = "hourly";
+
+ /**
+ * The ONCE policy means that the artifact retrieval occurs only if the
+ * local artifact is not present. This means that the retreival can only
+ * occur once.
+ */
+ public static final String ONCE = "once";
+
+ private Set validPolicyCodes = new HashSet();
+
+ public AbstractUpdatePolicy()
+ {
+ validPolicyCodes.add( IGNORED );
+ validPolicyCodes.add( DISABLED );
+ validPolicyCodes.add( DAILY );
+ validPolicyCodes.add( HOURLY );
+ validPolicyCodes.add( ONCE );
+ }
+
+ protected abstract boolean isSnapshotPolicy();
+
+ public boolean applyPolicy( String policySetting, Properties request, File localFile )
+ {
+ String version = request.getProperty( "version", "" );
+ boolean isSnapshotVersion = false;
+
+ if( StringUtils.isNotBlank( version ) )
+ {
+ isSnapshotVersion = VersionUtil.isSnapshot( version );
+ }
+
+ // Test for mismatches.
+ if ( !isSnapshotVersion && isSnapshotPolicy() )
+ {
+ getLogger().debug( "Non-snapshot version detected in during snapshot policy. ignoring policy.");
+ return true;
+ }
+
+ if ( isSnapshotVersion && !isSnapshotPolicy() )
+ {
+ getLogger().debug( "Snapshot version detected in during release policy. ignoring policy.");
+ return true;
+ }
+
+ if ( !validPolicyCodes.contains( policySetting ) )
+ {
+ // No valid code? false it is then.
+ getLogger().error( "Unknown artifact-update policyCode [" + policySetting + "]" );
+ return false;
+ }
+
+ if ( IGNORED.equals( policySetting ) )
+ {
+ // Disabled means no.
+ getLogger().debug( "OK to update, policy ignored." );
+ return true;
+ }
+
+ if ( DISABLED.equals( policySetting ) )
+ {
+ // Disabled means no.
+ getLogger().debug( "NO to update, disabled." );
+ return false;
+ }
+
+ if ( !localFile.exists() )
+ {
+ // No file means it's ok.
+ getLogger().debug( "OK to update, local file does not exist." );
+ return true;
+ }
+
+ if ( ONCE.equals( policySetting ) )
+ {
+ // File exists, but policy is once.
+ getLogger().debug( "NO to update, local file exist (and policy is ONCE)." );
+ return false;
+ }
+
+ if ( DAILY.equals( policySetting ) )
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.add( Calendar.DAY_OF_MONTH, -1 );
+ Calendar fileCal = Calendar.getInstance();
+ fileCal.setTimeInMillis( localFile.lastModified() );
+
+ return cal.after( fileCal );
+ }
+
+ if ( HOURLY.equals( policySetting ) )
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.add( Calendar.HOUR, -1 );
+ Calendar fileCal = Calendar.getInstance();
+ fileCal.setTimeInMillis( localFile.lastModified() );
+
+ return cal.after( fileCal );
+ }
+
+ getLogger().error( "Unhandled policyCode [" + policySetting + "]" );
+ return false;
+ }
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+import org.codehaus.plexus.digest.ChecksumFile;
+import org.codehaus.plexus.digest.Digester;
+import org.codehaus.plexus.digest.DigesterException;
+import org.codehaus.plexus.logging.AbstractLogEnabled;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+public class ChecksumPolicy
+ extends AbstractLogEnabled
+ implements PostDownloadPolicy
+{
+ /**
+ * The FAIL policy indicates that if the checksum does not match the
+ * downloaded file, then remove the downloaded artifact, and checksum
+ * files, and fail the transfer to the client side.
+ */
+ public static final String FAIL = "fail";
+
+ /**
+ * The FIX policy indicates that if the checksum does not match the
+ * downloaded file, then fix the checksum file locally, and return
+ * to the client side the corrected checksum.
+ */
+ public static final String FIX = "fix";
+
+ /**
+ * The IGNORE policy indicates that the checksum is never tested
+ * and even bad downloads and checksum files are left in place
+ * on the local repository.
+ */
+ public static final String IGNORED = "ignored";
+
+ /**
+ * @plexus.requirement role-hint="sha1"
+ */
+ private Digester digestSha1;
+
+ /**
+ * @plexus.requirement role-hint="md5"
+ */
+ private Digester digestMd5;
+
+ /**
+ * @plexus.requirement
+ */
+ private ChecksumFile checksumFile;
+
+ private Set validPolicyCodes = new HashSet();
+
+ public ChecksumPolicy()
+ {
+ validPolicyCodes.add( FAIL );
+ validPolicyCodes.add( FIX );
+ validPolicyCodes.add( IGNORED );
+ }
+
+ public boolean applyPolicy( String policySetting, Properties request, File localFile )
+ {
+ if ( !validPolicyCodes.contains( policySetting ) )
+ {
+ // No valid code? false it is then.
+ getLogger().error( "Unknown checksum policyCode [" + policySetting + "]" );
+ return false;
+ }
+
+ if ( IGNORED.equals( policySetting ) )
+ {
+ // Ignore.
+ return true;
+ }
+
+ if ( !localFile.exists() )
+ {
+ // Local File does not exist.
+ getLogger().debug( "Local file " + localFile.getAbsolutePath() + " does not exist." );
+ return false;
+ }
+
+ File sha1File = new File( localFile.getAbsolutePath() + ".sha1" );
+ File md5File = new File( localFile.getAbsolutePath() + ".md5" );
+
+ if ( FAIL.equals( policySetting ) )
+ {
+ if ( !sha1File.exists() && !md5File.exists() )
+ {
+ getLogger().error( "File " + localFile.getAbsolutePath() + " has no checksum files (sha1 or md5)." );
+ localFile.delete();
+ return false;
+ }
+
+ // Test for sha1 first, then md5
+
+ if ( sha1File.exists() )
+ {
+ try
+ {
+ return checksumFile.isValidChecksum( sha1File );
+ }
+ catch ( FileNotFoundException e )
+ {
+ getLogger().warn( "Unable to find sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to process sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to process sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ }
+
+ if ( md5File.exists() )
+ {
+ try
+ {
+ return checksumFile.isValidChecksum( md5File );
+ }
+ catch ( FileNotFoundException e )
+ {
+ getLogger().warn( "Unable to find md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to process md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to process md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ }
+ }
+
+ if ( FIX.equals( policySetting ) )
+ {
+ if ( !sha1File.exists() )
+ {
+ try
+ {
+ checksumFile.createChecksum( localFile, digestSha1 );
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to create sha1 file: " + e.getMessage(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to create sha1 file: " + e.getMessage(), e );
+ return false;
+ }
+ }
+ else
+ {
+ try
+ {
+ checksumFile.isValidChecksum( sha1File );
+ }
+ catch ( FileNotFoundException e )
+ {
+ getLogger().warn( "Unable to find sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to process sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to process sha1 file: " + sha1File.getAbsolutePath(), e );
+ return false;
+ }
+ }
+
+ if ( !md5File.exists() )
+ {
+ try
+ {
+ checksumFile.createChecksum( localFile, digestMd5 );
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to create md5 file: " + e.getMessage(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to create md5 file: " + e.getMessage(), e );
+ return false;
+ }
+ }
+ else
+ {
+ try
+ {
+ return checksumFile.isValidChecksum( md5File );
+ }
+ catch ( FileNotFoundException e )
+ {
+ getLogger().warn( "Unable to find md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( DigesterException e )
+ {
+ getLogger().warn( "Unable to process md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ catch ( IOException e )
+ {
+ getLogger().warn( "Unable to process md5 file: " + md5File.getAbsolutePath(), e );
+ return false;
+ }
+ }
+ }
+
+ getLogger().error( "Unhandled policyCode [" + policySetting + "]" );
+ return false;
+ }
+
+ public String getDefaultPolicySetting()
+ {
+ return FIX;
+ }
+
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * DownloadPolicy
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public interface DownloadPolicy
+{
+ /**
+ * The IGNORED policy means that the policy is ignored.
+ */
+ public static final String IGNORED = "ignored";
+
+ /**
+ * Get the default policy setting.
+ *
+ * @return the default policy setting.
+ */
+ public String getDefaultPolicySetting();
+
+ /**
+ * Apply the download policy.
+ *
+ * @param policySetting the policy setting.
+ * @param request the list of request properties that the policy might use.
+ * @param localFile
+ *
+ * @return true if the policy passes.
+ */
+ public boolean applyPolicy( String policySetting, Properties request, File localFile );
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * Policy to apply after the download has completed, but before the
+ * resource is made available to the calling client.
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public interface PostDownloadPolicy extends DownloadPolicy
+{
+ /**
+ * Apply the download policy.
+ *
+ * A true result allows the download to succeed. false indicates that the
+ * download is a failure.
+ *
+ * @param policySetting the policy setting.
+ * @param request the list of request properties that the policy might use.
+ * @param localFile the local file that this policy affects
+ *
+ * @return true if the policy passes. false if not.
+ */
+ public boolean applyPolicy( String policySetting, Properties request, File localFile );
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+
+import java.io.File;
+import java.util.Properties;
+
+/**
+ * Policy to apply before the download is attempted.
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public interface PreDownloadPolicy extends DownloadPolicy
+{
+ /**
+ * Apply the download policy.
+ *
+ * A true result lets the download occur. A false result prevents the download
+ * from occuring.
+ *
+ * @param policySetting the policy setting.
+ * @param request the list of request properties that the policy might use.
+ * @param localFile the local file that this policy affects
+ *
+ * @return true if the policy passes. false if not.
+ */
+ public boolean applyPolicy( String policySetting, Properties request, File localFile );
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * {@link PreDownloadPolicy} to apply for released versions.
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ *
+ * @plexus.component role="org.apache.maven.archiva.policies.download.PreDownloadPolicy"
+ * role-hint="releases"
+ */
+public class ReleasesPolicy
+ extends AbstractUpdatePolicy
+ implements PreDownloadPolicy
+{
+ public String getDefaultPolicySetting()
+ {
+ return AbstractUpdatePolicy.IGNORED;
+ }
+
+ protected boolean isSnapshotPolicy()
+ {
+ return false;
+ }
+}
--- /dev/null
+package org.apache.maven.archiva.policies;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * {@link PreDownloadPolicy} to apply for snapshot versions.
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ *
+ * @plexus.component role="org.apache.maven.archiva.policies.download.PreDownloadPolicy"
+ * role-hint="releases"
+ */
+public class SnapshotsPolicy
+ extends AbstractUpdatePolicy
+ implements PreDownloadPolicy
+{
+ public String getDefaultPolicySetting()
+ {
+ return AbstractUpdatePolicy.IGNORED;
+ }
+
+ protected boolean isSnapshotPolicy()
+ {
+ return true;
+ }
+}