1   package com.atlassian.maven.plugins.amps.product;
2   
3   import static com.atlassian.maven.plugins.amps.util.ConfigFileUtils.replace;
4   import static com.atlassian.maven.plugins.amps.util.ZipUtils.unzip;
5   import static com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory.output;
6   
7   import java.io.File;
8   import java.io.IOException;
9   import java.net.Socket;
10  import java.util.Arrays;
11  import java.util.Collection;
12  import java.util.Collections;
13  import java.util.HashMap;
14  import java.util.Map;
15  
16  import org.apache.maven.plugin.MojoExecutionException;
17  import org.apache.maven.plugin.logging.Log;
18  import org.apache.maven.project.MavenProject;
19  import org.apache.tools.ant.taskdefs.Java;
20  import org.apache.tools.ant.types.Path;
21  
22  import com.atlassian.maven.plugins.amps.MavenGoals;
23  import com.atlassian.maven.plugins.amps.Product;
24  import com.atlassian.maven.plugins.amps.ProductArtifact;
25  import com.atlassian.maven.plugins.amps.util.ant.AntJavaExecutorThread;
26  import com.atlassian.maven.plugins.amps.util.ant.JavaTaskFactory;
27  
28  public class FeCruProductHandler extends AbstractProductHandler
29  { 	
30      private static final int STARTUP_CHECK_DELAY = 1000;
31      private static final String FISHEYE_INST = "fisheye.inst";
32      
33      private final Log log;
34      private final JavaTaskFactory javaTaskFactory;
35      
36      public FeCruProductHandler(MavenProject project, MavenGoals goals, Log log)
37      {
38          super(project, goals, new FeCruPluginProvider());
39          this.log = log;
40          this.javaTaskFactory = new JavaTaskFactory(log);
41      }
42  
43      public String getId()
44      {
45          return ProductHandlerFactory.FECRU;
46      }
47  
48      public int getDefaultHttpPort()
49      {
50          return 3990;
51      }
52  
53      public final void stop(Product ctx) throws MojoExecutionException
54      {
55          log.info("Stopping " + ctx.getInstanceId() + " on ports "
56                  + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
57          try
58          {
59              execFishEyeCmd("stop", ctx);
60          }
61          catch (Exception e)
62          {
63              throw new MojoExecutionException("Failed to stop FishEye/Crucible instance at " + ctx.getServer() + ":" + ctx.getHttpPort());
64          }
65          
66          waitForFishEyeToStop(ctx);
67      }
68  
69      @Override
70      protected final void extractProductHomeData(File productHomeZip, File homeDir, Product ctx)
71              throws MojoExecutionException
72      {
73          try
74          {
75              unzip(productHomeZip, homeDir.getPath());
76          }
77          catch (final IOException ex)
78          {
79              throw new MojoExecutionException("Unable to copy home directory", ex);
80          }
81      }
82  
83      @Override
84      protected final void processHomeDirectory(Product ctx, final File homeDir) throws MojoExecutionException
85      {
86          //setup config.xml, ports, test repos, whatever
87          final File configXml = new File(homeDir, "config.xml");
88          replace(configXml, "@CONTROL_BIND@", String.valueOf(controlPort(ctx.getHttpPort())));
89          replace(configXml, "@HTTP_BIND@", String.valueOf(ctx.getHttpPort()));
90          replace(configXml, "@HTTP_CONTEXT@", String.valueOf(ctx.getContextPath()));
91          replace(configXml, "@HOME_DIR@", String.valueOf(homeDir.getAbsolutePath()));
92          replace(configXml, "@SITE_URL@", String.valueOf(siteUrl(ctx)));
93      }
94  
95      @Override
96      protected File extractApplication(Product ctx, File homeDir) throws MojoExecutionException
97      {
98          File appDir = createDirectory(getAppDirectory(ctx));
99          
100         final File cruDistZip = goals.copyDist(getBuildDirectory(),
101                 new ProductArtifact(
102                         "com.atlassian.crucible",
103                         "atlassian-crucible",
104                         ctx.getVersion()));
105         
106         try
107         {
108             unzip(cruDistZip, appDir.getPath(), 1);
109         }
110         catch (final IOException ex)
111         {
112             throw new MojoExecutionException("Unable to extract application ZIP artifact", ex);
113         }
114 
115         return appDir;
116     }
117 
118     private File getAppDirectory(Product ctx)
119     {
120         return new File(getBaseDirectory(ctx), ctx.getId() + "-" + ctx.getVersion());
121     }
122     
123     @Override
124     protected final ProductArtifact getTestResourcesArtifact()
125     {
126           return new ProductArtifact("com.atlassian.fecru", "amps-fecru", "LATEST");
127     }
128     
129     @Override
130     protected final Map<String, String> getSystemProperties(final Product ctx)
131     {
132         return new HashMap<String, String>()
133         {{
134             put(FISHEYE_INST, getHomeDirectory(ctx).getAbsolutePath());
135         }};
136     }
137 
138     @Override
139     protected String getBundledPluginPath(Product ctx)
140     {
141         return "plugins/bundled-plugins.zip";
142     }
143 
144     @Override
145     protected Collection<? extends ProductArtifact> getDefaultBundledPlugins()
146     {
147         return Collections.emptySet();
148     }
149 
150     @Override
151     protected Collection<? extends ProductArtifact> getDefaultLibPlugins()
152     {
153         return Collections.emptySet();
154     }
155 
156     @Override
157     protected final File getUserInstalledPluginsDirectory(File appDir, File homeDir)
158     {
159         return new File(new File(new File(homeDir, "var"), "plugins"), "user");
160     }
161 
162     @Override
163     protected boolean supportsStaticPlugins()
164     {
165         return false;
166     }
167 
168     @Override
169     protected final int startApplication(Product ctx, final File app, final File homeDir, Map<String, String> properties) throws MojoExecutionException
170     {
171         if (ctx.getJvmArgs() == null)
172         {
173             ctx.setJvmArgs("-Xmx512m -XX:MaxPermSize=160m");
174         }
175 
176         log.info("Starting " + ctx.getInstanceId() + " on ports "
177                 + ctx.getHttpPort() + " (http) and " + controlPort(ctx.getHttpPort()) + " (control)");
178 
179         AntJavaExecutorThread thread;
180         try
181         {
182             thread = execFishEyeCmd("run", ctx);
183         }
184         catch (Exception e)
185         {
186             throw new MojoExecutionException("Error starting fisheye.", e);
187         }
188 
189         waitForFishEyeToStart(ctx, thread);
190         
191         return ctx.getHttpPort();
192     }
193 
194     private void waitForFishEyeToStart(Product ctx, AntJavaExecutorThread thread) throws MojoExecutionException
195     {
196         boolean connected = false;
197         int waited = 0;
198         while (!connected)
199         {
200             try
201             {
202                 Thread.sleep(STARTUP_CHECK_DELAY);
203             }
204             catch (InterruptedException e)
205             {
206                 // ignore
207             }
208             try
209             {
210                 new Socket("localhost", ctx.getHttpPort()).close();
211                 connected = true;
212             }
213             catch (IOException e)
214             {
215                 // ignore
216             }
217 
218             if (thread.isFinished())
219             {
220                 throw new MojoExecutionException("Fisheye failed to start.", thread.getBuildException());
221             }
222             
223             if (waited++ * STARTUP_CHECK_DELAY > ctx.getStartupTimeout())
224             {
225                 throw new MojoExecutionException("FishEye took longer than " + ctx.getStartupTimeout() + "ms to start!");
226             }
227         }
228     }
229 
230     private void waitForFishEyeToStop(Product ctx) throws MojoExecutionException
231     {
232         boolean connected = true;
233         int waited = 0;
234         while (connected)
235         {
236             try
237             {
238                 Thread.sleep(STARTUP_CHECK_DELAY);
239             }
240             catch (InterruptedException e)
241             {
242                 // ignore
243             }
244             try
245             {
246                 new Socket("localhost", ctx.getHttpPort()).close();
247             }
248             catch (IOException e)
249             {
250                 connected = false;
251             }
252 
253             if (waited++ * STARTUP_CHECK_DELAY > ctx.getShutdownTimeout())
254             {
255                 throw new MojoExecutionException("FishEye took longer than " + ctx.getShutdownTimeout() + "ms to stop!");
256             }
257         }
258     }
259 
260     private AntJavaExecutorThread execFishEyeCmd(String bootCommand, final Product ctx) throws MojoExecutionException
261     {
262         final Map<String, String> properties = mergeSystemProperties(ctx);
263         
264         Java java = javaTaskFactory.newJavaTask(
265                 output(ctx.getOutput()).
266                 systemProperties(properties).
267                 jvmArgs(ctx.getJvmArgs()));
268 
269         Path classpath = java.createClasspath();
270         classpath.createPathElement().setLocation(new File(getAppDirectory(ctx), "fisheyeboot.jar"));
271         
272         java.setClassname("com.cenqua.fisheye.FishEyeCtl");
273         
274         java.createArg().setValue(bootCommand);
275         
276         AntJavaExecutorThread javaThread = new AntJavaExecutorThread(java);
277         javaThread.start();
278 
279         return javaThread;
280     }
281     
282     private File getBuildDirectory()
283     {
284         return new File(project.getBuild().getDirectory());
285     }
286 
287     private String siteUrl(Product ctx)
288     {
289         return "http://" + ctx.getServer() + ":" + ctx.getHttpPort() + ctx.getContextPath();
290     }
291 
292     /**
293      * The control port is the httpPort with a "1" appended to it //todo doc this
294      */
295     private int controlPort(int httpPort)
296     {
297         return httpPort * 10 + 1;
298     }
299 
300     private static class FeCruPluginProvider extends AbstractPluginProvider
301     {
302 
303         @Override
304         protected Collection<ProductArtifact> getSalArtifacts(String salVersion)
305         {
306             return Arrays.asList(
307                     new ProductArtifact("com.atlassian.sal", "sal-api", salVersion),
308                     new ProductArtifact("com.atlassian.sal", "sal-fisheye-plugin", salVersion)
309             );
310         }
311     }
312 }