github.com/apache/beam/sdks/v2@v2.48.2/java/container/pathingjar.go (about) 1 // Licensed to the Apache Software Foundation (ASF) under one or more 2 // contributor license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright ownership. 4 // The ASF licenses this file to You under the Apache License, Version 2.0 5 // (the "License"); you may not use this file except in compliance with 6 // the License. You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package main 17 18 import ( 19 "archive/zip" 20 "fmt" 21 "io" 22 "os" 23 "strings" 24 ) 25 26 // makePathingJar produces a context or 'pathing' jar which only contains the relative 27 // classpaths in its META-INF/MANIFEST.MF. 28 // 29 // Since we build with Java 8 as a minimum, this is the only supported way of reducing 30 // command line length, since argsfile support wasn't added until Java 9. 31 // 32 // https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html is the spec. 33 // 34 // In particular, we only need to populate the Jar with a Manifest-Version and Class-Path 35 // attributes. 36 // Per https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#classpath 37 // the classpath URLs must be relative for security reasons. 38 func makePathingJar(classpaths []string) (string, error) { 39 f, err := os.Create("pathing.jar") 40 if err != nil { 41 return "", fmt.Errorf("unable to create pathing.jar: %w", err) 42 } 43 defer f.Close() 44 if err := writePathingJar(classpaths, f); err != nil { 45 return "", fmt.Errorf("unable to write pathing.jar: %w", err) 46 } 47 return f.Name(), nil 48 } 49 50 var lineBreak = []byte{'\r', '\n'} 51 var continuation = []byte{' '} 52 53 func writePathingJar(classpaths []string, w io.Writer) error { 54 jar := zip.NewWriter(w) 55 defer jar.Close() 56 57 if _, err := jar.Create("META-INF/"); err != nil { 58 return fmt.Errorf("unable to create META-INF/ directory: %w", err) 59 } 60 61 zf, err := jar.Create("META-INF/MANIFEST.MF") 62 if err != nil { 63 return fmt.Errorf("unable to create META-INF/MANIFEST.MF: %w", err) 64 } 65 66 zf.Write([]byte("Manifest-Version: 1.0")) 67 zf.Write(lineBreak) 68 zf.Write([]byte("Created-By: sdks/java/container/pathingjar.go")) 69 zf.Write(lineBreak) 70 // Class-Path: must have a sequence of relative URIs for the paths 71 // which we assume outright in this case. 72 73 // We could do this memory efficiently, but it's not worthwhile compared to the complexity 74 // at this stage. 75 allCPs := "Class-Path: file:" + strings.Join(classpaths, " file:") 76 77 const lineLenMax = 71 // it's actually 72, but we remove 1 to account for the continuation line prefix. 78 buf := make([]byte, lineLenMax) 79 cur := 0 80 for cur+lineLenMax < len(allCPs) { 81 next := cur + lineLenMax 82 copy(buf, allCPs[cur:next]) 83 zf.Write(buf) 84 zf.Write(lineBreak) 85 zf.Write(continuation) 86 cur = next 87 } 88 zf.Write([]byte(allCPs[cur:])) 89 zf.Write(lineBreak) 90 return nil 91 }